Popular Posts

Protect Your REST APIs and PII with JWT

How it works
Simple way to Protect your REST APIs and PII (Personally Identifiable Information) using JWT Tokens.

How it works

To protect your customer data (PII like card number) that is exchanged between two parties/channels JWT Token are helpful.

HTTPS only provides network level security but data still visible to the uses who gains authorization to the system.

plain-api.sh
$/> curl https://yourdomain.com/customers

[
    {
        "id": 1,
        "name": "James",
        "social": "999-99-9999",
        "phonoe": "111-111-1111",
    }
]

Mask Sensitive information of API response for data security.

masking_senstive_info.sh
$/> curl https://yourdomain.com/customers

[
    {
        "id": "Q-VQ7HafDZ8aD6J_UJD62G3iUMyZBSGguf3pSBnLCgQh7FhA==",
        "name": "James",
        "social": "####-##-9999",
        "phonoe": "XXX-XXXX-1111",
    }
]

Steps to implement JWT Library

Create simple POJO With Sensitive Information Attributes

Create simple POJO class which contains sensitive information you want to protect.

This helps you develop REST API which contains sensitive information which are accessible by different customers and also protects your API by enabling token based authentication.

PersonalInfo contains

  1. Social (sensitive)

  2. Phone Number (sensitive information)

  3. Card Number (sensitive)

CustomerInfo.java
package com.tvajjala.security.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.tvajjala.security.annotation.Obfuscate;
import com.tvajjala.security.constants.Strategy;

import java.time.LocalDate;

/**
 * object that contains sensitive  information
 *
 * @author ThirupathiReddy  Vajjala
 */
public class CustomerInfo extends WebToken {


    @Obfuscate(strategy = Strategy.CARD)
    @JsonProperty("card")
    private String card;

    @JsonProperty("ssn")
    @Obfuscate(strategy = Strategy.SSN)
    private String social;

    @JsonProperty("phn")
    @Obfuscate(strategy = Strategy.PHONE)
    private String phone;


    public String getCard() {
        return card;
    }

    public void setCard(final String card) {
        this.card = card;
    }


    public String getSocial() {
        return social;
    }

    public void setSocial(final String social) {
        this.social = social;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(final String phone) {
        this.phone = phone;
    }
}

Create Base Class

This based class should be used by JWT library to place sensitive information in encrypted format. and also contains attributes which verifies token expiration time , generation time and issuer.

Tip
read more about JWT specification supported claims here and https://jwt.io/introduction/
WebToken.java
package com.tvajjala.security.model;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.time.Instant;
import java.time.temporal.ChronoUnit;


public class WebToken {

    @JsonProperty("sub")
    private String subject;


    @JsonProperty("iat")
    private final long issuedAt = Instant.now().getEpochSecond();


    @JsonProperty("exp")
    private final long expiredAt = Instant.now().minus(15, ChronoUnit.MINUTES).getEpochSecond();


    public String getSubject() {
        return subject;
    }

    public void setSubject(final String subject) {
        this.subject = subject;
    }
}

Create Enum to Support Different Types

Each sensitive information has separate format and length, to support different masking patterns , this ENUM will helpful.

MaskingStrategy.java
/**
 * @author ThirupathiReddy Vajjala
 */
public enum MaskingStrategy {

    PHONE, SSN, ACCOUNT, DEFAULT
}

Create Annotation which you add it your POJO attributes.

This annotate will helps seamless encryption and decryption your sensitive information .

Obfuscate.java
package com.tvajjala.security.annotation;

import com.tvajjala.security.constants.Strategy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author ThirupathiReddy Vajjala
 */
@Retention(RetentionPolicy.RUNTIME)//available source, compile and runtime
@Target(ElementType.FIELD)//add this annotation on field
public @interface Obfuscate {


    /**
     * masking strategy
     *
     * @return
     */
    Strategy strategy() default Strategy.DEFAULT;

    /**
     * encrypt the content
     *
     * @return true or false , default it is true
     */
    boolean enc() default true;
}

add @Obfuscate annotation to attributes which has sensitive information.

Tip
annotation processor inside this utility will read all attributes and encrypt and add it to JWT subject.

perform signing on this JSON payload with secretKey

Note
nimbusds library used to generate token and signing and verifying token

Generate Symmetric Encryption Keys

KeyGeneration.java
package com.tvajjala.security.util;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.SecureRandom;
import java.util.Base64;

public class KeyGeneration {

    public static void main(String[] args) throws Exception{


        SecureRandom random = new SecureRandom();
        byte[] sharedSecret = new byte[32];
        random.nextBytes(sharedSecret);

        System.out.println("signingKey  "+ Base64.getUrlEncoder().encodeToString(sharedSecret));


        byte[] iv = new byte[16];
        random.nextBytes(iv);
        System.out.println("IV key:   "+Base64.getUrlEncoder().encodeToString(iv));


        KeyGenerator keyGenerator=KeyGenerator.getInstance("AES");

        keyGenerator.init(128);

        SecretKey secretKey= keyGenerator.generateKey();

        System.out.println("dataKey : "+Base64.getUrlEncoder().encodeToString(secretKey.getEncoded()));
    }
}
Note
These symmetric keys shared between two parties.
security.properties
# Signing and verifying

siginingKey= VDaYcuLDYcX1x88tyBAFSAKvqfZTATAC2pn1umqoRjE=

# Encrypting Data
dataEncryptionKey= aflyv0OvtWOlstbS1Udc0IYS+fD3Vq+FXyai2cBE2+k=

#Initialization Vector of data encryption
iv= GYym8ew1diYNl6Fv86ECg==

Client side token generating and signing

ClientSideSigning.java
        byte[] sharedSecret=Base64
                            .getDecoder()
                            .decode("VDaYcuLDYcX1x88tyBAFSAKvqfZTATAC2pn1umqoRjE=");

        JWSSigner signer = new MACSigner(sharedSecret);

        JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload(jsonPayload));

        // Apply the HMAC siginig
        jwsObject.sign(signer); (1)
  1. Client side Signing

Server side token verification

ServerSideVerification.java
  JWSVerifier verifier = new MACVerifier(sharedSecret);

  jwsObject.verify(verifier);// if TRUE - trusted source (1)
  1. Verify the token at server side, it could be Servlet Filter.

Masking Strategies

Strategy.java
public enum Strategy {

    PHONE("xxx-xxx-1234"),
    SSN("xxx-xxx-9999"),
    ACCOUNT_NUMBER("xxxxxx1234"),
    CREDIT_CARD("xxxxxxxxxxxx1234"),
    DEFAULT
}
sampleWebToken
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJRLVZRN0hhZkRaOGFENkpfZUlDa1hIWG5UckNqZVExeEtlOEIyQ3p6c2VjRjkyeE9kUDdLQV9VSkQ2MkczaVVNeVpCU0dndWYzcFNCbkxDZ1FoN0ZoQT09IiwiaWF0IjoxNTkzNjE4ODk3LCJleHAiOjE1OTM2MTc5OTcsInRybiI6IlhYWFhYWFhYWFg4Mzg4MzgiLCJzc24iOiIjIyMtIyMtOTk5OSJ9.0DGMFJ2KKKmsCHcd5H32JXbh_1DyNRatDv0TgfNjy3Q
Tip
Point your browser to https://jwt.io/ and paste above token and see how it looks like.
claims.json
{
  "sub": "Q-VQ7HafDZ8aD6J_eICkXHXnTrCjeQ1xKe8B2CzzsecF92xOdP7KA_UJD62G3iUMyZBSGguf3pSBnLCgQh7FhA==", (1)
  "iat": 1593618897, (2)
  "exp": 1593617997, (3)
  "trn": "XXXXXXXXXX838838", (4)
  "ssn": "###-##-9999" (5)
}
  1. Subject contains sensitive information in encrypted format

  2. Issued Date of the token

  3. Expiry Date of the token

  4. Sensitive information in masked format Ex: card number

  5. Sensitive information in masked format Ex: Social

Authorization Header

Pass this token as Authorization header for downstream calls.

Authorization Header
Authorization:   Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJRLVZRN0hhZkRaOGFENkpfZUlDa1hIWG5UckNqZVExeEtlOEIyQ3p6c2VjRjkyeE9kUDdLQV9VSkQ2MkczaVVNeVpCU0dndWYzcFNCbkxDZ1FoN0ZoQT09IiwiaWF0IjoxNTkzNjE4ODk3LCJleHAiOjE1OTM2MTc5OTcsInRybiI6IlhYWFhYWFhYWFg4Mzg4MzgiLCJzc24iOiIjIyMtIyMtOTk5OSJ9.0DGMFJ2KKKmsCHcd5H32JXbh_1DyNRatDv0TgfNjy3Q

No comments:

Post a Comment