Let’s Try Salesforce Login Forensics

By March 20, 2017 Blog No Comments

Salesforce continues to provide higher security in their update releases. This blog will go over one of the new updates in Spring ’16: Login Forensics.

What is Login Forensics?

The help document describes it as below:

Given the number of logins to an org on a daily—even hourly—basis, security practitioners can find it tough to determine if a specific user account is compromised.

Login forensics helps you identify suspicious login activity. It provides you key user access data, including:

  • The average number of logins per user per a specified time period
  • Who logged in more than the average number of times
  • Who logged in during non-business hours
  • Who logged in using suspicious IP ranges

It is quite similar to a Login History feature except that statistics are now reported automatically. Another nice thing is that you can extend Login Event’s HTTP header when logging in via Custom Application API.

Login Event and statistic data that are retrieved by Login Forensic will be provided as API object LoginEvent and PlatformEventMetrics. Please be aware that the data cannot be seen from Salesforce standard page (View or Report).

Enable Login Forensics

Go to Setting -> Monitor -> Logs -> Event Monitoring Setup.

Enable Login Forensics.

lets-try-salesforce-login-forensics-1

Make a sample Application

Let’s make a sample application that exports LoginEvent/PlatformEventMetrics data into CSV.

1. Get WSDL

Go to Settings -> Build -> Create -> generate Enterprise WSDL from API. Save WSDL.

2. Download Force WSC(Force.com Web Service Connector)

Download the latest version of WSC from the link below:

http://mvnrepository.com/artifact/com.force.api/force-wsc

3. Generate stub

Use the command below to generate stub.

java -classpath wsc-XX.jar com.sforce.ws.tools.wsdlc enterprise.wsdl enterprise.jar

4. Download Apache Commons CSV

We will use Apache Commons CSV for easier output. Download from the link below:

http://commons.apache.org/proper/commons-csv/

Get LoginEvent

This is sample code to retrieve LoginEvent and export it as CSV. Even though it is an API object, it does not require special coding. You can retrieve data just like other Standard/Custom objects.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

package app;

 

import java.io.FileWriter;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.List;

import org.apache.commons.csv.CSVFormat;

import org.apache.commons.csv.CSVPrinter;

import org.apache.commons.csv.QuoteMode;

 

import com.sforce.soap.enterprise.Connector;

import com.sforce.soap.enterprise.EnterpriseConnection;

import com.sforce.soap.enterprise.QueryResult;

import com.sforce.soap.enterprise.sobject.LoginEvent;

import com.sforce.soap.enterprise.sobject.SObject;

import com.sforce.ws.ConnectionException;

import com.sforce.ws.ConnectorConfig;

 

public class LoginEventApp {

     

    static final String USERNAME = “hoge@hoge.com”;

    static final String PASSWORD = “password”;

    static EnterpriseConnection connection;

    static final String SOQL =

            “SELECT “

            + “AdditionalInfo, “

            + “ApiType, “

            + “ApiVersion, “

            + “Application, “

            + “AuthServiceId, “

            + “Browser, “

            + “ClientVersion, “

            + “EventDate, “

            + “LoginGeoId, “

            + “LoginHistoryId, “

            + “LoginType, “

            + “LoginUrl, “

            + “Platform, “

            + “SourceIp, “

            + “Status, “

            + “UserId, “

            + “Username “

            + “FROM “

            + “LoginEvent”;

     

    public static void main(String[] args) {

        ConnectorConfig config = new ConnectorConfig();

        config.setUsername(USERNAME);

        config.setPassword(PASSWORD);

        try{

            connection = Connector.newConnection(config);

            List<LoginEvent> loginEvents = queryLoginEvent();

            printCsv(loginEvents);

        }catch(ConnectionException e){

            e.printStackTrace();

        }

    }

 

    private static List<LoginEvent> queryLoginEvent() {

        List<LoginEvent> loginEvents = new ArrayList<LoginEvent>();

        try {

            Boolean done = false;

            QueryResult qr = connection.query(SOQL);

            if(qr.getSize() > 0){

                while(!done){

                    for(SObject sobj : qr.getRecords()){

                        loginEvents.add((LoginEvent)sobj);

                    }

                    if(qr.isDone()){

                        done = true;

                    }else{

                        qr = connection.queryMore(qr.getQueryLocator());

                    }

                }

            }

        } catch (ConnectionException e) {

            e.printStackTrace();

        }

        return loginEvents;

    }

     

    private static void printCsv(List<LoginEvent> loginEvents){

        CSVFormat format = CSVFormat.RFC4180.withDelimiter(‘,’).withQuoteMode(QuoteMode.ALL);

        CSVPrinter printer = null;

        try{

            Path path = Paths.get(“test.csv”);

            printer = new CSVPrinter(new FileWriter(path.toString()), format);

            for(LoginEvent le: loginEvents){

                printer.print(le.getAdditionalInfo());

                printer.print(le.getApiType());

                printer.print(le.getApiVersion());

                printer.print(le.getApplication());

                printer.print(le.getAuthServiceId());

                printer.print(le.getBrowser());

                printer.print(le.getClientVersion());

                printer.print(new SimpleDateFormat(“yyyy/MM/dd HH:mm:dd.ss”).format(le.getEventDate().getTime()));

                printer.print(le.getLoginGeoId());

                printer.print(le.getLoginHistoryId());

                printer.print(le.getLoginType());

                printer.print(le.getLoginUrl());

                printer.print(le.getSourceIp());

                printer.print(le.getPlatform());

                printer.print(le.getStatus());

                printer.print(le.getUser());

                printer.print(le.getUsername());

                printer.println();

            }

            printer.flush();

        }catch(Exception e){

            e.printStackTrace();

        }finally {

            if(printer != null){

                try{

                    printer.close();

                }catch(Exception e){

                    e.printStackTrace();

                }

            }

        }

    }

 

}

This is the CSV data. The data is similar to the information from Login History.

lets-try-salesforce-login-forensics-2

For more information, please see the Developer Guide:

https://developer.salesforce.com/docs/atlas.en-us.200.0.api.meta/api/sforce_api_objects_loginevent.htm

Get PlatformEventMetrics

Let’s retrieve PlatformEventMetics and export it as CSV. I thought it was going to be as easy as the LoginEvent, however it turned out that PlatformEventMetrics was not included in the WSDL!

My testing environment was a Developer Edition, and the WSDL was API version 36.0 so nothing should go wrong. I called Salesforce for a help.

[Comment from the Salesforce support]

We found out that the object applicable to Login Forensic is LoginEvent only. PlatformEventMetrics is not yet officially available.

Oh no! It looks like the there was a mistake in the release notes.

So it turned out that I could not complete the sample application. Here is the list of output data of I was hoping to see.

Metric Type(API reference name) Description
NumLogins Count of Login
NumDistinctLogins Count of distinct logins
NumLoginsByUser Count of logins by user
NumDistinctIps Count of distinct IPs
NumDistinctIpsByUser Count of distinct IPs by user
NumDistinctUsersByIP Count of distinct users by IP
NumDistinctBrowsersByUser Count of distinct browsers by user
NumDistinctUsersByBrowser Count of distinct users by browser
NumDistinctUsersByApplication Count of distinct users by application
NumDistinctApplicationsByUser Count of distinct applications by user
NumDistinctUsersByLoginUrl Count of distinct users by login URL
NumDistinctLoginUrlsByUser Count of distinct LoginUrls by user
NumDistinctUsersByPlatform Count of distinct users by platform
NumDistinctPlatformsByUser Count of distinct platforms by user

For more information, please see the Developer Guide:

https://developer.salesforce.com/docs/atlas.en-us.200.0.api.meta/api/sforce_api_objects_platformeventmetrics.htm

Closing

It is too bad that I could not complete the test since the most important feature of Login Forensics is to retrieve statistics. I will keep my eyes open for any changes and hope to report again when it is officially supported.

Thanks for reading!