JSON Web Token(JWT)

Format

A JSON Web Token (JWT) is a piece of secure information that is passed from one system to another, typically a web browser to a server. It contains enough information to identify some person or other entity, some claims (properties) about them, and a digital signature to confirm the information has not been tampered with.

The three bits of the JWT are separated by dots and Base 64 encoded. Base 64 encoding limits the characters to [A-Z0-9+/] which means the encoding is printable but unreadable.

<base64 encoded header>.<base64 encoded payroll>.<signature>

Enter a JWT and see the decoding components below:

Header

The JOSE (JSON Object Signing and Encryption) Header indicates this is a signed message.

  • "kid" (key identifier) hints at what key was used to secure the JWT
  • "alg" is the name of the cryptographic algorithm used to secure the JWT.
    {{ structure.header | prettyJSON }}

    Payload

    The payload contains of a set of claims including

  • "sub" (subject) the principal about which the claims are normally made.
  • "aud" (audience) which must be the name of the API/application.
  • "exp" (expiry)
  • "iat" (issued at) which gives the age An ID token authenticates a user and has information needed to customize a UI, email, picture and phone; it should not be used to give access to an application. A access token on the other hand does not authenticate a user but says the bearer of the token has been given access to the API and perform a set of actions given by the 'scopes'.
    {{ structure.payload | prettyJSON }}

    Signature

    The signature is created from the original header and payload. If the signature does not match the header and payload in the message then they are not the original.

    {{ structure.signature }}
    Sig = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
  • Browser Storage and Vulnerabilities.

    Local storage

    In a web browser, each domain and port (URL) has an associated table of key value pairs (of 5MB of storage) called local storage. This is very easy to use with Javascript:

    localStorage.setItem(key, value) const value = localStorage.getItem(key)

    It has no expiry so that when a user re-opens that URL (domain and port) the data will still be there. It cannot be accessed by other sites with another 'domain and port' or from localhost. Another user logging into the same machine (e.g. laptop or server) have no access. Unencrypted data in local storage can be vulnerable to Cross site scripting (XSS) attacks. On the other hand, since no data is loaded up to the server and it provides a way for an application to work with a user's PPI without privacy or GDPR compliance issues.

    The contents of your current local storage:

    Key Value
    {{ key }} {{ item }}
    {{ cookie }}

    Session storage

    Session storage is like local storage but expires when web browser tabs are closed.

    The contents of your current session storage:

    Key Value
    {{ key }} {{ item }}

    Cookies

    A cookie is created when a server sends a 'Set-Cookie' response header in an HTTP response to a client:

    HTTP/2.0 200 OK Content-Type: text/html Set-Cookie: cookie_key=cookie_value_up_to_4Kb Set-Cookie: sid=abc; Expires=Thu 18 Nov 2022 01:01:00 GMT; Secure; HttpOnly

    The client sends back all cookies with every subsequent request:

    GET /sample_page.html HTTP/2.0 Host: www.example.org Cookie: cookie_key=cookie_value_up_to_4Kb;sid=abc

    The 'Expires' flag helps prevent cookies being reused later in a session fixation attack. The 'Secure' flag indicates a cookie can only be sent of HTTPS and never over insecure HTTP, avoiding man in the middle attacks. The 'HttpOnly' flag indicates that a Javascript program can never see the cookie, helping to avoid XSS attacks. By default a cookie will only ever be sent to the server/domain it came from, the 'Domain' attribute indicates other domains. The 'Path' attribute says the cookie will only be sent with requests that have a particular Path in their URL. With 'SameSite=Strict' the cookie will only be sent to it's origin and never with cross-origin requests.

    From Javascript one can get a list of semi-colon separated cookies using 'document.cookie'.

    Your current cookies:

    Key Value
    {{ key }} {{ item }}

    Java Web Token in Cookies

    A JWT can be placed in session storage and sent in the Authorization field of requests. However, session storage may be vulnerable to XSS attacks. To avoid this the JWT can be stored as a cookie with the HttpOnly flag, so that it will be sent with every request to the origin but not be visible to Javascript.

    Summary

    Session and local storage can only be read on the client side and are <5MB while cookies are < 4KB. Cookies and session storage have automatic expiry (session storage until browser or tab is closed).

    XSS, CSFR and CORS

    Cross-site scripting (XSS) allows an attacker to execute arbitrary Javascript within a victim's browser.

    The following URL includes Javascript in the query string. Ideally the website should always 'escape' string properly to avoid these ever being interpretted as code, unfortunately this is not always done consistently. In particular since websites rely on so many third party Javascript libraries which may have their own vulnerabilities. https://insecure-website.com/status?message=<script>... Naughty Javascript Here ...</script>

    Reflected XSS

    Given a URL: https://insecure-website.com/search?term=stuff

    and a web page that displays the search term: <p>Results for: { term } </p>

    Given an attackers URL: https://insecure-website.com/search?term=<script>{ doBadStuff() }</script>

    The web page will call the dodgy code: <p>Results for: <script>{ doBadStuff() }</script> </p>

    Stored XSS

    ...

    DOM XSS

    ...

    CSRF

    ...

    Cross-site request forgery (CSRF) gets a victim to perform an action unwittingly.

    A CSRF Token is a random string generated by a server and sent to the client. Each client request would include the CSRF Token so that the server can indeed confirm each request came from the client. Typically you want to include the CSRF Token in the request body, or a custom header, and not the query string, as the latter are often logged. Since Cookies are sent automatically with all requests placing a CSRF token in a Cookie, means it would be sent with all request from the browser including those you don't want ...

    Cross-origin requests (CORS)

    ...

    OAuth 2.0 and OpenID Connect

    With a traditional client and server model, the developers have to write the application and takes care of authentication and authorization. With OAuth 2.0 and OpenID Connect, the idea is to decouple identity management so the server just has to worry about providing the application, while specialist software such Cognito takes care of identity authentication. Authentication is confirming users are who they say they are while authorization is about saying what they can do. Authentication is about identity while authorization is about privilleges.

    Oauth 2.0

    Oauth 2.0 is an authorization standard. It introduces quite a lot of jargon and terminology that needs to be understood.

    A Resource Owner typically an end user but in general any entity that can grant access to a resource.

    A Client is the actual application requesting access to the resource which is not necessarilly the end-user client or web-browser.

    A User Agent is typically a browser, a program used by the resource owner to access the Client.

    An Authorization Server issues Access Tokens (in JWT format) after authenticating the Resource Owners.

    A Resource Server hosts the API and protected resource that the Client wants access to, essentially it is the 'application'. When a Resource Server receives a JWT from a client, it has to check the signature on the JWT is valud, that it has not expired and ... If the the identity is disabled or deleted, there is no way to know this from the JWT, the Resource Server can either rely on the expiry period being short or go to the expense of asking the Authentication Server.

    Claim
    An user attribute, name/value pair. Profile claims include: name, family_name, locale, website, picture. A "piece of information asserted about an Entity"..
    Scope
    Space-separated identifiers that say what privilleges or actions are requested. See RFC 6749. For example 'email' requests access to email and email_verified claims. The 'openid' scope is always required.
    Relying party application (aka claims-aware application)
    Grant
    One can confuse Grant with OpenID Connect Flow. A flow is a protocol, while I believe a Grant refers only to a credential used withing a flow. From the specification (RFC 6749):
    "An authorization grant is a credential representing the resource owner's authorization ... used by the client to obtain an access token. This specification defines four grant types -- authorization code, implicit, resource owner password credentials, and client credentials ..."

    Open ID Connect

    OpenID Connect is an identity layer on top of Oauth 2.0. A Client can ask an Oauth2.0 Authorization Server to authenticate or verify the identity of an end-user and also return some profile information about the end-user.

    Each 'Flow' is different variant of the protocol to get an Access Token from the Authorization Server.

  • A cron job accessing an API would uses the client id and client secret to get hold of an Access Token but would have no need end-user authorization, so use the Client Credentials Flow. In this case the Client is the Resource Owner.
  • If the Client is a web server, use the Authorization Code Flow which passes the Access Token directly to the server without going through the web browser.
  • If Resource Owner Password Flow can be used if the Client has and can be trusted with the username and password.
  • If the Client is a Single Page Application (SPA) then use Authorization Code Flow with Proof Key for Code Exchange (PKCE).
  • If application is a mobile app then can also use Authorization Code Flow with Proof Key for Code Exchange (PKCE).

    Security Assertion Markup Language (SAML)

    SAML supports single sign-on (SSO) allowing one set of credentials to login to many websites. Allows identity providers (IdP) to pass authorization credentials to service providers (SP).

    SAML Identity Provider (IdP)
    manages identities and sercurity tokens (known as an Authorization Server in Oath2)
    SAML Servicer Provider
    the actual application (known as aResource Server in Oauth2)
  • Endpoints and Examples

    Oauth2 and OpenID Connect define the following API.

    The following bash script provides examples of use:

    Symmetric Encrypt and Decrypt with Javascript

    This example uses the SubtleCrypto package for symmetric encryption using AES (Advanced Encryption Standard).

    AES modes include: CTR (Counter Mode), CBC (Cipher Block Chaining), GCM (Galois/Counter Mode). This example uses: CBC.

    ...

    Plain text

    Secret

    Symmetric encryption means using the same secret/password for both encryption and decryption.

    Encrypted Cipher text

    Plaintext is the unencrypted text while ciphertext is the encrypted text, typically ciphertext is binary data and can only be displayed as numbers of random characters.

    Decrypted plain text

    This is the result for encryption followed by decryption and ought to be identical to the plaintext above.

    Javascript