IBM Security Verify

 View Only
  • 1.  How to secure an API using an apikey with WebSEAL

    Posted Tue February 04, 2020 09:46 AM
    We are trying to reduce the attack surface of some public APIs by checking a static header variable called "apikey".
    We tried using the new functionalities under "Secure Web Settings -> API Access Control -> Policies". But unfortunately it seems that using a policy a group membership is mandatory. As the API is public this doesn't cover our needs. Nonetheless it was interesting to see which changes were made to the WebSEAL config file, http transformations, ACL, POP and objectspace.

    As I understood it it should be possible to do such a check by adding the following two extended attributes to the POP:

    eas-trigger = trigger_attr_eas
    requires = apikey='xxxxx'

    In the [azn-decision-info] stanza we added an entry "apikey = header:apikey"

    And just to be sure we also added in the [user-attribute-definitions] stanza the values "apikey.category = Subject" and "apikey.datatype = string".

    In the pdweb.wan.azn log we can see that this attribute is added somewhere:

    2020-02-04-13:06:41.897+00:00I----- thread(21) trace.pdweb.wan.azn:9 /build/isam/src/i4w/pdwebrte/webcore/amw_azn.c:117: [148.110.128.146] Attr Name:apikey , Value: xxxxx

    In the pdweb.azn.attr log the attribute is also present, but not as a credential attribute:

    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:4 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:418: azn_svc_decision_access_allowed_ext protected_resource[/WebSEAL_API/webseal/@host/GET/@host/configs]
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:216: attributeList [app_context]:apikey=[[xxxxx]] AZN_EAS_POP_ATTRS_ATTRIBUTE=[[eas-trigger::::trigger_attr_eas][requires::::apikey='xxxxx']] AZN_EAS_POP_LOCATION_ATTRIBUTE=[[/WebSEAL_API/webseal/@host/GET/@host/configs]]
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:219: The total number of [app_context] attributes is 3.
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:216: attributeList [cred_attributes]:AUTHENTICATION_LEVEL=[[0]] AZN_CRED_AUTHNMECH_INFO=[[LDAP Registry]] AZN_CRED_AUTHZN_ID=[[cn=ttt]] AZN_CRED_AUTH_EPOCH_TIME=[[1580825260]] AZN_CRED_AUTH_METHOD=[[password]] AZN_CRED_BROWSER_INFO=[[tt]] AZN_CRED_GROUPS=[ttt] AZN_CRED_GROUP_REGISTRY_IDS=[ttt] AZN_CRED_GROUP_UUIDS=[ttt] AZN_CRED_IP_FAMILY=[[AF_INET]] AZN_CRED_MECH_ID=[[IV_LDAP_V3.0]] AZN_CRED_NETWORK_ADDRESS_BIN=[[tt]] AZN_CRED_NETWORK_ADDRESS_STR=[[tt]] AZN_CRED_PRINCIPAL_DOMAIN=[[Default]] AZN_CRED_PRINCIPAL_NAME=[[tt]] AZN_CRED_PRINCIPAL_UUID=[[tt]] AZN_CRED_QOP_INFO=[[SSK: TLSV13: 01]] AZN_CRED_REGISTRY_ID=[[cn=tt]] AZN_CRED_USER_INFO=[[]] AZN_CRED_VERSION=[[0x00000907]] SMS_SESSION_REALM=[[ISAM-Distributed-Session-Cache]] tagvalue_login_user_name=[[tt]] tagvalue_max_concurrent_web_sessions=[[unset]] tagvalue_session_index=[[tt]] tagvalue_user_session_id=[[tt]
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:219: The total number of [cred_attributes] attributes is 25.
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:308: Checking authorization with [apikey='xxxxx']
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:346: Checking the credential attribute list for apikey == xxxxx
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:356: Attribute is not present. Continuing to check the rest of the rule.
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:374: Finished checking the authorization. This rule has failed
    2020-02-04-14:15:13.944+00:00I----- thread(11) trace.pdweb.azn.attr:1 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:476: azn_svc_decision_access_allowed_ext decision [not permitted]

    ---------------------------------------------------------------------------------------------------------------

    To be able to better identify the value of the apikey which came from the request we changed it to yyyyy and that value indeed appears in this log, but not as a credential attribute:

    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:4 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:418: azn_svc_decision_access_allowed_ext protected_resource[/WebSEAL_API/webseal/@host/GET/@host/configs]
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:216: attributeList [app_context]:apikey=[[yyyyy]] AZN_EAS_POP_ATTRS_ATTRIBUTE=[[eas-trigger::::trigger_attr_eas][requires::::apikey='xxxxx']] AZN_EAS_POP_LOCATION_ATTRIBUTE=[[/WebSEAL_API/webseal/@host/GET/@host/configs]]
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:219: The total number of [app_context] attributes is 3.
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:216: attributeList [cred_attributes]:AUTHENTICATION_LEVEL=[[0]] AZN_CRED_AUTHNMECH_INFO=[[LDAP Registry]] AZN_CRED_AUTHZN_ID=[[tt]] AZN_CRED_AUTH_EPOCH_TIME=[[1580825260]] AZN_CRED_AUTH_METHOD=[[password]] AZN_CRED_BROWSER_INFO=[[tt]] AZN_CRED_GROUPS=[tt] AZN_CRED_GROUP_REGISTRY_IDS=[tt] AZN_CRED_GROUP_UUIDS=[tt] AZN_CRED_IP_FAMILY=[[AF_INET]] AZN_CRED_MECH_ID=[[IV_LDAP_V3.0]] AZN_CRED_NETWORK_ADDRESS_BIN=[[tt]] AZN_CRED_NETWORK_ADDRESS_STR=[[tt]] AZN_CRED_PRINCIPAL_DOMAIN=[[Default]] AZN_CRED_PRINCIPAL_NAME=[[tt]] AZN_CRED_PRINCIPAL_UUID=[[tt]] AZN_CRED_QOP_INFO=[[SSK: TLSV13: 01]] AZN_CRED_REGISTRY_ID=[[tt]] AZN_CRED_USER_INFO=[[]] AZN_CRED_VERSION=[[0x00000907]] SMS_SESSION_REALM=[[ISAM-Distributed-Session-Cache]] tagvalue_login_user_name=[[tt]] tagvalue_max_concurrent_web_sessions=[[unset]] tagvalue_session_index=[[tt]] tagvalue_user_session_id=[[tt]]
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:7 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:219: The total number of [cred_attributes] attributes is 25.
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:308: Checking authorization with [apikey='xxxxx']
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:346: Checking the credential attribute list for apikey == xxxxx
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:356: Attribute is not present. Continuing to check the rest of the rule.
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:6 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:374: Finished checking the authorization. This rule has failed
    2020-02-04-14:10:00.498+00:00I----- thread(5) trace.pdweb.azn.attr:1 /build/isam/src/i4w/pdwebrte/azn/attr-eas/amw_attr_eas.cpp:476: azn_svc_decision_access_allowed_ext decision [not permitted]

    ---------------------------------------------------

    So what is the correct way to check this attribute using an POP? Or is this only possible with Advanced Access Control?

    Thanks in advance and kind regards,

    Laurent

    ------------------------------
    Laurent Asselborn
    ------------------------------


  • 2.  RE: How to secure an API using an apikey with WebSEAL

    Posted Wed February 05, 2020 04:52 AM
    Hi Laurent,

    The "requires" check in the POP can only operate on credential attributes of the logged in user.  It can't check HTTP headers etc.
    The setup you have done to make the appKey header an "attribute" would allow you to check that attribute in an AAC policy.   So, as you guessed, AAC is one way you could enforce this.

    One word of warning though... checking the AAC policy on every request would be very inefficient. You would probably need to set decision caching so that once the header is checked for a session, it is not checked again.  This implies that you have authenticated sessions - I don't know if that is the case.  Maybe not since you're using the API key for access control.

    An alternative option (which I think would be lighter), would be to add an HTTP Transformation Rule to your WebSEAL which checks for the correct value in the apikey header and, if NOT found, re-writes the requested URL (so that it goes to an error page) or directly returns a static error response.  I'm not so good on HTTP Transformation rules but maybe someone else here can suggest a suitable rule.

    Jon.

    ------------------------------
    Jon Harry
    Consulting IT Security Specialist
    IBM
    ------------------------------



  • 3.  RE: How to secure an API using an apikey with WebSEAL

    Posted Wed February 05, 2020 10:00 AM
    Hi Jon,

    Interesting thought with the HTTP Transformation. I wrote a rule which should do the trick, but somehow it doesn't work as expected. I still get the requested file instead of an error message.
    Here is my transformation:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0" xmlns:external="http://xsltfunctions.isam.ibm.com">

    <xsl:strip-space elements="*" />
    <xsl:template match="/">
    <HTTPRequestChange />
    <xsl:variable name="apikey" select="//HTTPRequest/Headers/Header[@name = 'apikey']" />
    <xsl:if test="not($apikey = 'abc')">
    <HTTPResponseChange action="replace">
    <Version>HTTP/1.1</Version>
    <StatusCode>403</StatusCode>
    <Reason>apikey missing or wrong</Reason>
    <Header name="Content-Type" action="add">text%2Fhtml%3Bcharset%3DUTF-8</Header>
    <Body>%3Ch1%3EError%3C%2Fh1%3E%0A%3Cp%3Eapikey%20missing%20or%20empty%3C%2Fp%3E</Body>
    </HTTPResponseChange>
    </xsl:if>
    </xsl:template>
    </xsl:stylesheet>

    -----------------------------------------------
    and here is the result in pdweb.http.transformation.log:

    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:9 /build/isam/src/i4w/pdweb/webseald/http/transformation/HTTPTransformationEngine.cpp:267: HTTPTransformationEngine::processRequestByResource Resultant XMLHTTPRequest: <?xml version="1.0" encoding="UTF-8"?><HTTPRequestChange xmlns:external="http://xsltfunctions.isam.ibm.com"/><HTTPResponseChange xmlns:external="http://xsltfunctions.isam.ibm.com" action="replace"><Version>HTTP/1.1</Version><StatusCode>403</StatusCode><Reason>apikey missing or wrong</Reason><Header name="Content-Type" action="add">text%2Fhtml%3Bcharset%3DUTF-8</Header><Body>%3Ch1%3EError%3C%2Fh1%3E%0A%3Cp%3Eapikey%20missing%20or%20empty%3C%2Fp%3E</Body></HTTPResponseChange>
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPRequest.cpp:165: ENTER XMLHTTPRequest::applyChanges
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPRequest.cpp:241: ENTER XMLHTTPRequest::processRequestLine (<HTTPResponseChange xmlns:external="http://xsltfunctions.isam.ibm.com" action="replace"><Version>HTTP/1.1</Version><StatusCode>403</StatusCode><Reason>apikey missing or wrong</Reason><Header name="Content-Type" action="add">text%2Fhtml%3Bcharset%3DUTF-8</Header><Body>%3Ch1%3EError%3C%2Fh1%3E%0A%3Cp%3Eapikey%20missing%20or%20empty%3C%2Fp%3E</Body></HTTPResponseChange>, 369)
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:9 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPRequest.cpp:312: XMLHTTPRequest::processRequestLine Setting Version to HTTP/1.1
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPRequest.cpp:320: RETURN XMLHTTPRequest::processRequestLine
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPMessage.cpp:283: ENTER XMLHTTPMessage::processHeaders (<HTTPResponseChange xmlns:external="http://xsltfunctions.isam.ibm.com" action="replace"><Version>HTTP/1.1</Version><StatusCode>403</StatusCode><Reason>apikey missing or wrong</Reason><Header name="Content-Type" action="add">text%2Fhtml%3Bcharset%3DUTF-8</Header><Body>%3Ch1%3EError%3C%2Fh1%3E%0A%3Cp%3Eapikey%20missing%20or%20empty%3C%2Fp%3E</Body></HTTPResponseChange>, 369)
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:9 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPMessage.cpp:413: XMLHTTPMessage::processHeaders Adding header (Name = Content-Type, Value = text%2Fhtml%3Bcharset%3DUTF-8)
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPMessage.cpp:308: RETURN XMLHTTPMessage::processHeaders
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPMessage.cpp:456: ENTER XMLHTTPMessage::processCookies (<HTTPResponseChange xmlns:external="http://xsltfunctions.isam.ibm.com" action="replace"><Version>HTTP/1.1</Version><StatusCode>403</StatusCode><Reason>apikey missing or wrong</Reason><Header name="Content-Type" action="add">text%2Fhtml%3Bcharset%3DUTF-8</Header><Body>%3Ch1%3EError%3C%2Fh1%3E%0A%3Cp%3Eapikey%20missing%20or%20empty%3C%2Fp%3E</Body></HTTPResponseChange>, 369)
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPMessage.cpp:480: RETURN XMLHTTPMessage::processCookies
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/XMLHTTPRequest.cpp:232: RETURN XMLHTTPRequest::applyChanges
    2020-02-05-14:42:30.808+00:00I----- thread(3) trace.pdweb.http.transformation:6 /build/isam/src/i4w/pdweb/webseald/http/transformation/HTTPTransformationEngine.cpp:342: RETURN HTTPTransformationEngine::processRequestByResource
    -------------------------------------------------------------------
    So the rule was applied correctly but I still got the requested document instead of an error. Did I miss something?


    ------------------------------
    Laurent LA Asselborn
    ------------------------------



  • 4.  RE: How to secure an API using an apikey with WebSEAL

    Posted Thu February 06, 2020 06:03 AM
    Hi Laurent,

    I didn't actually verify your transformation rule in my lab. But if the rule is ok, then maybe there's something that caches the reply? So consider to add a cache-control HTTP header in the reply. Something like 
    <Header name="cache-control">max-age=0</Header>

    You never know...

    Kind regards, Peter.

    ------------------------------
    Peter Volckaert
    Senior Sales Engineer
    Authentication and Access
    IBM Security
    ------------------------------



  • 5.  RE: How to secure an API using an apikey with WebSEAL

    Posted Thu February 06, 2020 10:56 AM
    Hi Peter,

    No, no luck with that header. Somehow I don't get WebSEAL to directly send the response.

    I now tried a different approach which works, but is less nice. I used the AclBits Parameter to change the request:

    <?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:external="http://xsltfunctions.isam.ibm.com" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!--**************************************-->
    <!--WARNING: THIS FILE HAS BEEN CREATED AS PART OF THE API ACCESS CONTROL RESOURCE DEFINITION. DO NOT MODIFY UNLESS ABSOLUTELY NECESSARY.-->
    <!--**************************************-->
    <!--This stylesheet is used to manage the GET test.api.etat.lu/cigcc-config-jaxrs/gcconfig/configs resource.-->
    <!--Firstly, strip any space element-->
    <xsl:strip-space elements="*"/>
    <!--Perform a match on the root of the document. Output the required HTTPRequestChange elements and then process templates.-->
    <xsl:template match="/">
    <HTTPRequestChange>
    <xsl:apply-templates select="//HTTPRequest/Headers/Header[@name = 'accept']"/>
    <xsl:apply-templates select="//HTTPRequest/RequestLine"/>
    <!--Set the ACL bits that will be used for authorization for this resource.-->
    <xsl:variable name="apikey" select="//HTTPRequest/Headers/Header[@name = 'apikey']" />
    <xsl:choose>
    <xsl:when test="$apikey = 'abc'">
    <AclBits>Tr</AclBits>
    </xsl:when>
    <xsl:otherwise>
    <AclBits>T</AclBits>
    </xsl:otherwise>
    </xsl:choose>
    </HTTPRequestChange>
    </xsl:template>
    </xsl:stylesheet>
    --------------------------------------------------------------------
    This works but I would have preferred the other option as that gives me more control over the answer. And to use this I had to configure the http transformation in the config file and not in a POP.

    ------------------------------
    Laurent LA Asselborn
    ------------------------------



  • 6.  RE: How to secure an API using an apikey with WebSEAL

    Posted Fri February 07, 2020 09:51 AM
    Hi Laurent,

    I had a chance to look at this and I think I got the Transformation Rule working.  This is the rule I used:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <!-- Firstly, strip any space elements -->
    <xsl:strip-space elements="*" />
    <!-- Perform a match on the root of the document. Output the required HTTPRequestChange elements and then process templates. -->
    <xsl:template match="/">
    <HTTPRequestChange />
    <xsl:apply-templates />
    </xsl:template>
    <xsl:template match="//HTTPRequest">
    <xsl:variable name="apikey" select="Headers/Header[@name = 'apikey']" />
    <xsl:if test="not($apikey = 'abc')">
    <HTTPResponseChange action="replace">
    <Version>HTTP/1.1</Version>
    <StatusCode>403</StatusCode>
    <Reason>apikey missing or wrong</Reason>
    <Header name="Content-Type" action="add">text%2Fhtml%3Bcharset%3DUTF-8</Header>
    <Body>%3Ch1%3EError%3C%2Fh1%3E%0A%3Cp%3Eapikey%20missing%20or%20empty%3C%2Fp%3E</Body>
    </HTTPResponseChange>
    </xsl:if>
    </xsl:template>
    </xsl:stylesheet>

    Cheers... Jon.

    ------------------------------
    Jon Harry
    Consulting IT Security Specialist
    IBM
    ------------------------------



  • 7.  RE: How to secure an API using an apikey with WebSEAL

    Posted Fri February 07, 2020 11:08 AM
    Hi Jon,

    I also got the transformation to work. I'm now analyzing what the difference to my initial transformation is.

    Did you use a POP or configure the transformation via WebSEAL config? Now that it works I'm using WebSEAL config but at first I was using a POP.

    ------------------------------
    Laurent LA Asselborn
    ------------------------------



  • 8.  RE: How to secure an API using an apikey with WebSEAL

    Posted Fri February 07, 2020 11:27 AM

    Laurent,

    I configured in the config file. I thought that I tested your original rule in the config file first (and it didn't work - as you described) but I could be wrong.

    I also struggle to see the difference with my working rule. 


    Jon. 



    ------------------------------
    Jon Harry
    Consulting IT Security Specialist
    IBM
    ------------------------------



  • 9.  RE: How to secure an API using an apikey with WebSEAL

    Posted Mon February 10, 2020 05:19 AM
    After a lot of searching I found the, quite unlikely, culprit.
    It is:

    xmlns:external="http://xsltfunctions.isam.ibm.com"

    It seems this namespace was added in a recent upgrade. Our old http transformation rules don't have this namespace but newly created ones do. After deleting this entry the transformation works as expected.


    ------------------------------
    Laurent LA Asselborn
    ------------------------------