The article describes the approach that can be used for secure authentication and password management in FIXEdge with use of password encryption. The approach is an alternative to storing session's UserName and Password in FIXEdge.properties configuration file as a plain text.
Encryption for the Password tag (554) was added in FIXEdge 5.10; Encryption for the NewPassword tag (925) was added in FIXEdge 5.10.1. |
The approach assumes the following:
The idea of storing user credentials in a separate place can be implemented by means of History element of FIXEdge Business Layer. File history is considered below, however ODBC History (means Database) can be used for this purpose as well.
It is assumed that user credentials are stored per unique SenderCompID. The following text block should be added to BL_Config.xml file to configure the file history:
<History Name="Passwords" StorageType="File" WorkingDirectory="FIXEdge1/conf/" StorageFileName="Passwords"> <KeyFields>49, 56</KeyFields> <Fields>553, 554</Fields> </History> |
In this case the history file will appear in FIXEdge WorkingDirectory (i.e. FIXEdge1/conf/).
Originally FIXEdge administrator should assign user credentials for the particular client. This can be achieved by means of a special CONTROL-CENTER session. Please find the sample of such session below:
FixLayer.FixEngine.Session.ControlSession.Version = FIX44 FixLayer.FixEngine.Session.ControlSession.Role = Acceptor FixLayer.FixEngine.Session.ControlSession.SenderCompID = CENTER FixLayer.FixEngine.Session.ControlSession.TargetCompID = CONTROL FixLayer.FixEngine.Session.ControlSession.InSeqNum = 0 FixLayer.FixEngine.Session.ControlSession.OutSeqNum = 0 FixLayer.FixEngine.Session.ControlSession.Description = FIXEdge Control Session to manage user credentials FixLayer.FixEngine.Session.ControlSession.RecreateOnLogout = true FixLayer.FixEngine.Session.ControlSession.ForceReconnect = true FixLayer.FixEngine.Session.ControlSession.ForceSeqNumReset = 1 FixLayer.FixEngine.Session.ControlSession.IgnoreSeqNumTooLowAtLogon = true FixLayer.FixEngine.Session.ControlSession.RejectMessageWhileNoConnection = false FixLayer.FixEngine.Session.ControlSession.IntradayLogoutTolerance = true FixLayer.FixEngine.Session.ControlSession.StorageType = persistentMM |
Note, UserName and Password for Control Session (if they are required) will still need to be specified in plain text in FIXEdge.properties configuration file. However, the access to the Control Session can be limited, for example by source IP. |
The following rule should be specified in BL_Config configuration file in order to register new user credentials via CONTROL-CENTER session:
<Rule Description="AddSession2PasswordHistory" > <Source> <FixSession SenderCompID="CONTROL" TargetCompID="CENTER" /> </Source> <Condition> <EqualField Field="35" Value="BE"/> <EqualField Field="924" Value="3"/> </Condition> <Action> <SetField Field="56" Value="FIXEDGE" /> <CopyField SourceField="115" TargetField="49" IsRequiredField="Y"/> <HashField Field="554"/> <SaveToHistory Name="Passwords"/> <StopProcessing/> </Action> </Rule> |
This rule says that if User Request message (35 = BE) with UserRequestType = "Change Password For User" (924 = 3) comes from CENTER-CONTROL FIX session, it should encrypt the "Password" field and save the credentials to the "Passwords" history.
FIX Client Simulator can be used to setup CENTER-CONTROL FIX session with the FIXEdge server and send the messages over it. For example, this message can be used to setup username "TestClient1" with password "TestPassword1" for session CLIENT1.
The following rule performs users authentication and password update. In this example it is assumed that all the client FIX sessions have TargetCompID equal to "FIXEDGE":
<CreateSessionEvent> <Source> <FixSession SenderCompID=".*" TargetCompID="FIXEDGE"/> </Source> <Condition> <Exclusion> <Script Language="JavaScript" FileName ="FIXEdge1/conf/IsValidPassword.js"/> </Exclusion> </Condition> <CreateSessionAction> <RejectSession>Authentication error</RejectSession> </CreateSessionAction> </CreateSessionEvent> |
The JavaScript which is called from this rule returns bool value to accept or reject a session. It does the following:
Here is the code of this JavaScript which should be put to FIXEdge WorkingDirectory (i.e. FIXEdge1/conf/):
valid = false; sender = getStringField(49); target = getStringField(56); user = getStringField(553); passEncrypted = getStringField(554); // the value in Password (554) tag is encrypted newPassEncrypted = getStringField(925); // the value in NewPassword (925) tag is encrypted //Decrypts provided value and returns hash of it. Does nothing for null values function getPass(p) { if (p != null) return hashString(decryptString(p)); else return p; } //get hashs of values in Password (554) and NewPassword (554) tags inputPass = getPass(passEncrypted); newPass = getPass(newPassEncrypted); key = new Array(sender, target); savedUser = getFromHistory("Passwords", key, "553"); savedPass = getFromHistory("Passwords", key, "554"); reason = ""; if ( savedUser == null) { reason = "(Username is not found in history)"; } if ( savedUser == "N/A" ) // Accept the session if stored username is "N/A" without checking the credentials. { valid = true; } else if ( (user != null ) && (user == savedUser) ) // username is found in the history { if( (savedPass == null) || (savedPass == hashString("N/A")) || (inputPass == savedPass) ) // Accept the session if stored password is "N/A" or the same as in Logon { if( (newPass != null) && (newPass != savedPass) ) // Update the password in the history if NewPassword (925) is defined { values = new Array(); values.push(user); values.push(newPass); updateHistory("Passwords", key, values, ""); print("[NOTE] Password has been successfully changed for session " + sender + "-" + target + "."); } valid = true; } } if (valid == false) { print("[WARN] Invalid username/password combination has been received for session " + sender + "-" + target + ". "+ reason); } valid; |
Routing Rules and Session Events: XML Transformation Language