본문 바로가기

보안/시큐어코딩

ESAPI를 이용한 OWASP Top 취약점 예방

원본: http://amtuanand.blogspot.kr/2012/01/owasp-top-vulnerabilities-and.html


ESAPI를 이용한 OWASP Top 취약점 예방

 

ESAPI 아키텍처 구조



총 14개의 도메인으로 구성되어 있다. 각 도메인은 상호 유기적인 관계를 가지고 있으며, 라이브러리 사용을 위해 제공되는 인터페이스이다.




1. Authentication 


 

ESAPI Autentication 인터페이스는 계정의 자격증명이나 세션 구분자을 생성하거나 조작할 수 있는 메서드 모음을 정의한다. ESAPI의 authenticatior  클래스 객체는 다음과 같이 생성할 수 있다. 

 

Authenticator instance = ESAPI.authenticator();

 


사용자 생성


ESAPI에서 사용자를 생성하는 방법은 두가지 이다.

애플리케이션 안에서 다음과 같이 사용자를 생성할 수 있다. 

ESAPI.authenticator.createUser(username, password, password);

새로운 사용자를 생성하기 위한 정보를 제공한다. 생성할 사용자의 이름과 패스워드(패스워드는 brute force 공격에 취약하지 않은 강도를 가지고 있는지 점검한다. verifyAccountNameStrength(String), verifyPasswordStrength(String, String) )   새로운 패스워드의 두개 복사본은 사용자 계정생성시 입력되는  re-type password가 포함되도록 설계된 것이다.

 

Note: createUser()로 생성되는 사용자는 디폴트로 disabled 하거나 locked 시킬 수 있다.


ESAPI.authenticator().getUser(username).enable();
ESAPI.authenticator().getUser(username).unlock();


Login

디폴트 ESAPI authenticator를 사용하려면 로그인페이지는 SSL을 사용해야 하며, 서버는 SSL을 설정을 위한 keystore 파일을 가지고 있어야 한다.


인증을 사용하기 위해 다음과 같이 호출한다.

User user = ESAPI.authenticator().login(HTTPServletRequest, HTTPServletResponse);

UsernameParameterName과 PasswordParameterName 변수는 ESAPI.properties에 미리 설정되어 있어야 한다. login() 메서드는 HTTPRequest로 부터 username과 password값을 프로퍼티에 미리 설정된 변수명으로 참조한다.


Logout

사용자가 로그아웃을 하기 위해서는 다음과 같이 간단히 호출하면 된다.


User user = ESAPI.authenticator().logout;


ESAPI User Interface

ESAPI의 User 인터페이스는  응용프로그램이 보안을 강화하기 위해 각 사용자에 대해 저장해야 하는 많은 정보를 저장할 수 있도록 지원한다. 사용자 계정은 여러가지 상태중 하나 일 수 있다. 계정을 처음 만들때 사용자는 비활성화 되어야 하고,  만료되지 않아야 하고, 락되지 않아야 한다. 계정을 사용하려면 관리자는 계정을 사용가능하도록 설정해 주어야 한다. 계정이 락이 되는 경우는 너무많은 로그인 시도가 있은경우가 가장 일반적인 이유이다. 만료날짜가 되는 경우도 계정이 잠기게 된다. 사용자 인증을 통해 User는 enable, not expired, unlock 상태를 얻을 수 있다.


2. Session Management


Session management 는 컴퓨터 시스템과 상호 작용하는  사용자의 활동을  추적하는 과정이다.  ESAPI 의 HTTPUtilities 인터페이스는 HTTP 요청, 응답, 세션, 쿠키, 헤더, 로깅에 관련된 추가 보안을 제공하는 방법의 모음이다.


Session Fixation

인증할 때마다 응용 프로그램이 사용하는 세션 식별자를 변경해야 세션 하이재킹 공격의 취약점을 제거할 수 있다.  공격자가 다른 사용자의 세션식별자를 알고 있다면 해당 세션 식별자를 사용하여 다른 사용자의 권한으로 서비스를 받는것이 가능하게 된다. 처음 할당 받은 세션식별자가 변경되지 않고 세션동안 계속 유효하게 사용할 수 있는것을  "session fixation" 이라고 부른다.


사용자가 로그인 할대 세션 식별자를 변경하게 되면 쉽게 공격자를 무력화 시킬 수 잇다. 하지만 대부분의 플랫폼이 기본적으로 이 작업을 수행하지 않는다. ESAPI를 사용하여 사용하여 인증작업을 수행하게 되면 쉽게 "Session Fixed" 취약점을 제거할 수 있다.


ESAPI.httpUtilities().changeSessionIdentifier();


사용자가 로그아웃할 때 마다 세션 식별자가 서버에서 무효화 되고, 브라우저에서 클리어 되는지 확인해야 한다.

 


Cross Site Request Forgery

CSRF는 현재 인증되어 있는 사용자가 의도하지 않은 액션을 강제로 수행하게 하는 공격이다.  사회공학(이메일이나 링크등)의 도움으로 공격자는 공격자가 원하는 작업을 사용자가 실행하도록 한다. 성공적인 CSRF공격은 결국 사용자 데이터의 손상을 유발 할 수 있으며, 만약 공격 대상이 관리자라고 한다면 전체 웹 애플리케이션의 손상도 발생될 수 있다. CSRF 토큰을 사용하여 해당 요청이 실제 해당 사용자의 요청인지를 확인하는 단계를 추가하여 이 공격을 회피할 수 있다.

ESAPI.httpUtilities().addCSRFToken()

A CSRF 토큰은 세션마다 또는 하나의 요청마다 새로 생성하여 사용할 수 있다.

ESAPI.authenticator().getCurrentUser().resetCSRFToken()




3. Access Control


Access Control은 시스템에서 각 사용자의 권한을 정의하는 프로세스이다. ESaPI Access Controller 인터페이스는 액세스 제어를 수행하는 다양한 애플리케이션에서 사용할 수 있는 메서드들을 정의하고 있다.

 


Preventing Forced Browsing

 

AccessController 인터페이스는 액세스 제어를 수행하는 다양한 애플리케이션에서 사용할할 수 있는 다양한 메서드들을 정의하고 있다.  대부분의 애플리케이션에서 다양한 응용 계층의 여러 위치에서 수행되어야 한다.  이 클래스는 URL들이나, 비즈니스 기능, 데이터, 서비스, 파일을 위한 액세스 제어 기능을 제공한다.

 

ESAPI.accessController().isAuthorizedForURL(String url)
<-- 계정이 참조하는 URL에 접근 권한이 있으면 true를 반환한다. URL 접근 권한 룰은  .esapi\fbac-policies\URLAccessRules.txt  파일에 정의한다.

 

ESAPI Access Controller 인터페이스는 다음과 같은 메서드를 정의하고 있다.


 

  • assertAuthorizedForData(java.lang.String key) Checks if the current user is authorized to access the referenced data. This method simply returns if access is authorized.
  • assertAuthorizedForData(java.lang.String action, java.lang.Object data) Checks if the current user is authorized to access the referenced data.
  • assertAuthorizedForFile(java.lang.String filepath) Checks if an account is authorized to access the referenced file.
  • assertAuthorizedForFunction(java.lang.String functionName)Checks if an account is authorized to access the referenced function.
  • assertAuthorizedForService(java.lang.String serviceName) Checks if an account is authorized to access the referenced service.
  • assertAuthorizedForURL(java.lang.String url) Checks if an account is authorized to access the referenced URL.
  • boolean isAuthorizedForData(java.lang.String key) Checks if an account is authorized to access the referenced data, represented as a String.
  • boolean isAuthorizedForData(java.lang.String action, java.lang.Object data) Checks if an account is authorized to access the referenced data, represented as an Object.
  • boolean isAuthorizedForFile(java.lang.String filepath) Checks if an account is authorized to access the referenced file.
  • boolean isAuthorizedForFunction(java.lang.String functionName) Checks if an account is authorized to access the referenced function.
  • boolean isAuthorizedForService(java.lang.String serviceName) Checks if an account is authorized to access the referenced service.
  • boolean isAuthorizedForURL(java.lang.String url) Checks if an account is authorized to access the referenced URL.

Preventing Insecure Object References

 

개발자가 같은 URL이나 폼 매개 변수로 파일, 디렉토리, 데이터베이스 기록, 또는 키 같은 내부 구현 객체에 대한 참조를 노출 할 때 직접 객체 참조가 발생한다. 액세스 제어가 적절한 위치에서 이루어 지지 않으면 공격자는 권한없이 다른 객체를 액세스하는 직접 객체참조를 조작할 수 있다.

org.owasp.esapi.reference.RandomAccessReferenceMap을 사용하여 direct 참조에서 indirect 참조로 변경할 수 있다. 내부의 직접 객체 참조 집합을 안전한 간접 참조 집합 으로 매핑하는데 사용된다. 데이터베이스의 키나 파일명, 다른 타입의 직접 객체참조에 사용될 수 있다. 규칙에 의해 개발자는 공격자가 조작하려고 시도하는 직접 참조 객체에 대한 노출을 제거할 수 있다.

 



4. Input Validation


입력 유효성 검사는 프로그램이 정확하고 유용한 데이터에서 작동되도록 하는 프로세스이다 ESAPI validator 인터페이스는 신뢰할 수 없는 입력을 정규화하고 유효성 검사를 수행하는 메서드의 집합이다.  입력값의 유효성 검사는 애플리케이션 보안의 기본이다.


가장 일반적인 웹 애플케이션 보안 취약점은 클라이언트 또는 환경으로 부터 입력되는 입력값에 대한 유효성 검사가 제대로 이루어지지 않은 경우 발생한다. 클라이언트는 데이터를 조작할 수 있는 가능성이 있으므로 클라이언트에서 전송되는 데이터는 신뢰할 수 없다.


인코딩은 입력 유효성 검사의 부족에 의존하는 공격을 완화 시킬 수 있다. 브라우저로 전송되기 전에 사용자의 입력을 HTML 엔티티 인코딩을 사용하는 경우 대부분의 XSS 공격을 방지할 수 있다.  하지만 단순한 공격 예방만으로 충분하지 않으므로 침입탐지를 수행하는 기능이 애플리케이션에 포함되어야 한다. 


Validating User Input

Validator interfaces는 신뢰할 수 없는 입력을 정규화하고, 유효성을 검사하는 메서드의 집합을 정의한다.  이 인터페이스는 예외를 발생시키기 보다는 부울값을 결과로 반환한다. 

ESAPI.validator().getValidInput(String context,String input,String type,int maxLength,
                                               boolean allowNull,ValidationErrorList errorList)

이 메서드는 정규화되고 검증된 입력데이터를 문자열로 반환한다. 잘못된 입력은 ValidationException을 발생시키며, 명확하게 공격 데이터 인 경우 IntrusionException을 발생시킨다. 에러는 ValidationErrorList 에 저장된다. 

ESAPI.validator().getValidInput 은 validation.properties에 기술된 Validator.SafeString으로 정의된 정규표현식을 사용하여 검증한다. 

1. 입력은  ESAPI.validator().getValidInput() 을 사용하여 정규화와 검증 작업을 수행한다.

2. 출력은 XSS 방지하기 위해 ESAPI.encoder().encodeForHTML() 을 사용한다.

ESAPI's Validator Interface는 다음 메서드들을 포함한다.
  • void assertIsValidHTTPRequest() Validates the current HTTP request by comparing parameters, headers, and cookies to a predefined whitelist of allowed characters.
  • void assertIsValidHTTPRequestParameterSet(String context, Set required, Set optional) Validates that the parameters in the current request contain all required parameters and only optional ones in addition.
  • void assertIsValidHTTPRequestParameterSet(String context, Set required, Set optional, ValidationErrorList errorList) Validates that the parameters in the current request contain all required parameters and only optional ones in addition.
  • void assertValidFileUpload(String context, String filepath, String filename, byte[] content, int maxBytes, boolean allowNull) Validates the filepath, filename, and content of a file.
  • void assertValidFileUpload(String context, String filepath, String filename, byte[] content, int maxBytes, boolean allowNull, ValidationErrorList errorList) Validates the filepath, filename, and content of a file.
  • String getValidCreditCard(String context, String input, boolean allowNull) Returns a canonicalized and validated credit card number as a String.
  • String getValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errorList) Returns a canonicalized and validated credit card number as a String.
  • Date getValidDate(String context, String input, java.text.DateFormat format, boolean allowNull) Returns a valid date as a Date.
  • Date getValidDate(String context, String input, java.text.DateFormat format, boolean allowNull, ValidationErrorList errorList) Returns a valid date as a Date.
  • String getValidDirectoryPath(String context, String input, boolean allowNull) Returns a canonicalized and validated directory path as a String. Returns a canonicalized and validated directory path as a String.
  • String getValidDirectoryPath(String context, String input, boolean allowNull, ValidationErrorList errorList) Returns a canonicalized and validated directory path as a String.
  • Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) Returns a validated real number as a double.
  • Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errorList) Returns a validated real number as a double.
  • byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) Returns validated file content as a byte array.
  • byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errorList) Returns validated file content as a byte array.
  • String getValidFileName(String context, String input, boolean allowNull) Returns a canonicalized and validated file name as a String.
  • String getValidFileName(String context, String input, boolean allowNull, ValidationErrorList errorList) Returns a canonicalized and validated file name as a String.
  • String getValidInput(String context, String input, String type, int maxLength, boolean allowNull) Returns canonicalized and validated input as a String.
  • String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errorList) Returns canonicalized and validated input as a String.
  • Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) Returns a validated integer.
  • Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errorList) Returns a validated integer.
  • String getValidListItem(String context, String input, List list) Returns the list item that exactly matches the canonicalized input.
  • String getValidListItem(String context, String input, List list, ValidationErrorList errorList) Returns the list item that exactly matches the canonicalized input.
  • Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) Returns a validated number as a double within the range of minValue to maxValue.
  • Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errorList) Returns a validated number as a double within the range of minValue to maxValue.
  • byte[] getValidPrintable(String context, byte[] input, int maxLength, boolean allowNull) Returns canonicalized and validated printable characters as a byte array.
  • byte[] getValidPrintable(String context, byte[] input, int maxLength, boolean allowNull, ValidationErrorList errorList) Returns canonicalized and validated printable characters as a byte array.
  • String getValidPrintable(String context, String input, int maxLength, boolean allowNull) Returns canonicalized and validated printable characters as a String.
  • String getValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) Returns canonicalized and validated printable characters as a String.
  • String getValidRedirectLocation(String context, String input, boolean allowNull) Returns canonicalized and validated printable characters as a String.
  • String getValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errorList) Returns canonicalized and validated printable characters as a String.
  • String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull) Returns canonicalized and validated "safe" HTML.
  • String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) Returns canonicalized and validated "safe" HTML.
  • boolean isValidCreditCard(String context, String input, boolean allowNull) Returns true if input is a valid credit card.
  • boolean isValidDate(String context, String input, java.text.DateFormat format, boolean allowNull) Returns true if input is a valid date according to the specified date format.
  • boolean isValidDirectoryPath(String context, String input, boolean allowNull) Returns true if input is a valid directory path.
  • boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) Returns true if input is a valid double within the range of minValue to maxValue.
  • boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) Returns true if input is valid file content.
  • boolean isValidFileName(String context, String input, boolean allowNull) Returns true if input is a valid file name.
  • boolean isValidFileUpload(String context, String filepath, String filename, byte[] content, int maxBytes, boolean allowNull) Returns true if a file upload has a valid name, path, and content.
  • boolean isValidHTTPRequest() Validate the current HTTP request by comparing parameters, headers, and cookies to a predefined whitelist of allowed characters.
  • boolean isValidHTTPRequestParameterSet(String context, Set required, Set optional) Returns true if the parameters in the current request contain all required parameters and only optional ones in addition.
  • boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull) Returns true if input is valid according to the specified type.
  • boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) Returns true if input is a valid integer within the range of minValue to maxValue.
  • boolean isValidListItem(String context, String input, List list) Returns true if input is a valid list item.
  • boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) Returns true if input is a valid number within the range of minValue to maxValue.
  • boolean isValidPrintable(String context, byte[] input, int maxLength, boolean allowNull) Returns true if input contains only valid printable ASCII characters (32-126).
  • boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull) Returns true if input contains only valid printable ASCII characters (32-126).
  • boolean isValidRedirectLocation(String context, String input, boolean allowNull) Returns true if input is a valid redirect location, as defined by "ESAPI.properties".
  • boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull) Returns true if input is "safe" HTML.
  • boolean safeReadLine(InputStream inputStream, int maxLength) Reads from an input stream until end-of-line or a maximum number of characters.

One method many people use to safeguard their application from a XSS attack is to filter out the <script> tag. While this may seem like it would prevent an attack involving javascript, it does contain some flaws. One way to bypass this filtering is to input <scr<script>ipt>. When <script> is removed, the two halves of the other <script> tag will come together, forming an attack. Try this below and see what happens.
Use ESAPI.validator().getValidSafeHtml to prevent the Cross Site Scripting Attack.



5. Output Encoding/Escaping


Encoding is the process of transforming information from one format into another. The ESAPI Encoder interface contains a number of methods for decoding input and encoding output so that it will be safe for a variety of interpreters.

Encoding, closely related to Escaping is a powerful mechanism to help protect against many types of attack, especially injection attacks and Cross-site Scripting (XSS). Essentially, encoding involves translating special characters into some equivalent that is no longer significant in the target interpreter. So, for example, using HTML entity encoding before sending untrusted data into a browser will protect against many forms of Cross-site Scripting (XSS).

Considerations:

What interpreter?: To encode properly, you need to know what interpreters the data might end up in. For example, if the data is going into a SQL interpreter, you should consider encoding based on syntax of the SQL engine you are using.

What characters? Complete?:You want to make sure that you encode all the characters that might cause a problem, so the best approach is to use a positive encoding scheme, where all characters except a minimal known good set are encoded.

What encoding scheme?:There are dozens of ways to encode characters and many interpreters allow multiple forms of a single significant character. For a browser, HTML entity encoding is a good way to prevent script injection, but URL encoding or Unicode encoding (%xx) will not prevent scripts from running. Be sure to use the appropriate encoding scheme for the target interpreter.

Double encoding and decoding?:Be careful not to double encode your data. In some cases, doubly encoding data can inadvertently introduce special characters in the final output. Also, be aware that some processors may automatically undo your encoding. There is some evidence that XML processors are decoding HTML entity encoding, thus reintroducing potential XSS problems.

Encoding & Decoding

ESAPI's Encoder interface 입력 인코딩과 출력 인코딩을 위한 메서드를 정의한다.


// ESAPI's Encoder interface 사용예

 

ESAPI.encoder().encodeForHTML(input)


  • String canonicalize(String input) This method performs canonicalization on data received to ensure that it has been reduced to its most basic form before validation.
  • String canonicalize(String input, boolean strict) This method performs canonicalization on data received to ensure that it has been reduced to its most basic form before validation.
  • decodeFromBase64(String input) Decode data encoded with BASE-64 encoding.
  • decodeFromURL(String input) Decode from URL.
  • encodeForBase64(byte[] input, boolean wrap) Encode for Base64.
  • encodeForCSS(String input) Encode data for use in Cascading Style Sheets (CSS) content.
  • encodeForDN(String input) Encode data for use in an LDAP distinguished name.
  • encodeForHTML(String input) Encode data for use in HTML using HTML entity encoding.
  • encodeForHTMLAttribute(String input) Encode data for use in HTML attributes.
  • encodeForJavaScript(String input) Encode data for insertion inside a data value in JavaScript.
  • encodeForLDAP(String input) Encode data for use in LDAP queries.
  • encodeForOS(Codec codec, String input) Encode for an operating system command shell according to the selected codec (appropriate codecs include the WindowsCodec and UnixCodec).
  • encodeForSQL(Codec codec, String input) Encode input for use in a SQL query, according to the selected codec (appropriate codecs include the MySQLCodec and OracleCodec).
  • encodeForURL(String input) Encode for use in a URL.
  • encodeForVBScript(String input) Encode data for insertion inside a data value in a Visual Basic script.
  • encodeForXML(String input) Encode data for use in an XML element.
  • encodeForXMLAtribute(String input) Encode data for use in an XML attribute.
  • encodeForXPath(String input) Encode data for use in an XPath query.
  • normalize(String input) Reduce all non-ascii characters to their ASCII form so that simpler validation rules can be applied.



정규화작업을 수행한뒤 인코딩을 수행하려면 다음과 같이 사용한다.

ESAPI.encoder().canonicalize(input, true);



XSS Prevention

 

A. JavaScript Escape Before Inserting Untrusted Data into HTML JavaScript Data Values
     ESAPI.encoder().encodeForJavaScript(input31)


B. HTML Escape Before Inserting Untrusted Data into HTML Element Content
     ESAPI.encoder().encodeForHTML(input1)


C. Attribute Escape Before Inserting Untrusted Data into HTML Common Attributes
     ESAPI.encoder().encodeForHTMLAttribute(input21)

D. CSS Escape Before Inserting Untrusted Data into HTML Style Property Values
     ESAPI.encoder().encodeForCSS(input41)

E. URL Escape Before Inserting Untrusted Data into HTML URL Attributes
     ESAPI.encoder().encodeForURL(input51)




6. Cryptography


Encryption is the process of transforming information (referred to as plaintext) using an algorithm (called cipher) to make it unreadable to anyone except those possessing special knowledge, usually referred to as a key. The ESAPI Encryptor interface provides a set of methods for performing common encryption, random number, and hashing operations.
The failure to encrypt data passes up the guarantees of confidentiality, integrity, and accountability that properly implemented encryption conveys.


Consequences

Confidentiality: Properly encrypted data channels ensure data confidentiality.
Integrity: Properly encrypted data channels ensure data integrity.
Accountability: Properly encrypted data channels ensure accountability.

Risk
Omitting the use of encryption in any program which transfers data over a network of anykind should be considered on par with delivering the data sent to each user on the local networks of both the sender and receiver.
Worse, this omission allows for the injection of data into a stream of communication between two parties - with no means for the victims to separate valid data from invalid.

Encryption

Encryptor interface는 일반적인 암호화, 난수, 해시 작업을 수행하기 위한 메서드를 정의한다.

ESAPI encryption & decryption 메서든는 다음은 기본메서드를 사용한다.


java.lang.String decrypt(java.lang.String ciphertext)

java.lang.String encrypt(java.lang.String plaintext)


ESAPI's encryptor interface을 이용하여 Encryption & decryption 을 수행하려면 다음과 같이 사용한다.

encrypted = ESAPI.encryptor().encrypt( decrypted );
decrypted = ESAPI.encryptor().decrypt( encrypted );


Expiring Data

Create Seal

An integrity seal is created for the plain text entered by the user, the seal is set to be valid for 15 seconds by default.

ESAPI.encryptor.seal( plaintext, instance.getTimeStamp() + 1000 * Integer.parseInt(timer) );

Verify Seal
The call to the following method will return true if the seal is verified within 15 seconds.

ESAPI.encryptor.verifySeal( toVerify );


Unseal

The call to the following method will unseal it back to the plain text if it is done within 15 seconds.

ESAPI.encryptor.unseal(sealed);


ESAPI' Encryptor interface의 다음 메서드들은 Integrity Seals에 사용된다.

  • getRelativeTimeStamp(long offset) Gets an absolute timestamp representing an offset from the current time to be used by other functions in the library.
  • long getTimeStamp() Gets a timestamp representing the current date and time to be used by other functions in the library.
  • String hash(java.lang.String plaintext, java.lang.String salt) Returns a string representation of the hash of the provided plaintext and salt.
  • String seal(java.lang.String data, long timestamp) Creates a seal that binds a set of data and includes an expiration timestamp.
  • String sign(java.lang.String data) Create a digital signature for the provided data and return it in a string.
  • String unseal(java.lang.String seal) Unseals data (created with the seal method) and throws an exception describing any of the various problems that could exist with a seal, such as an invalid seal format, expired timestamp, or decryption error.
  • boolean verifySeal(java.lang.String seal) Verifies a seal (created with the seal method) and throws an exception describing any of the various problems that could exist with a seal, such as an invalid seal format, expired timestamp, or data mismatch.
  • boolean verifySignature(java.lang.String signature, java.lang.String data) Verifies a digital signature (created with the sign method) and returns the boolean result.


Randomness

Insecure randomness errors occur when a function that can produce predictable values is used as a source of randomness in security-sensitive context.

Computers are deterministic machines, and as such are unable to produce true randomness. Pseudo-Random Number Generators (PRNGs) approximate randomness algorithmically, starting with a seed from which subsequent values are calculated.
There are two types of PRNGs: statistical and cryptographic. Statistical PRNGs provide useful statistical properties, but their output is highly predictable and forms an easy to reproduce numeric stream that is unsuitable for use in cases where security depends on generated values being unpredictable. Cryptographic PRNGs address this problem by generating output that is more difficult to predict. For a value to be cryptographically secure, it must be impossible or highly improbable for an attacker to distinguish between it and a truly random value. In general, if a PRNG algorithm is not advertised as being cryptographically secure, then it is probably a statistical PRNG and should not be used in security-sensitive contexts.

Examples:


The following code uses a statistical PRNG to create generate a pseudo-random number.


int GenerateRandomNumber() {
    Random ranGen = new Random();
    ranGen.setSeed((new Date()).getTime());
    return (ranGen.nextInt(400000000));
}


This code uses the Random.nextInt() function to generate "unique" identifiers for the receipt pages it generates. Because Random.nextInt() is a statistical PRNG, it is easy for an attacker to guess the strings it generates. Although the underlying design of the receipt system is also faulty, it would be more secure if it used a random number generator that did not produce predictable receipt identifiers, such as a cryptographic PRNG.
The Randomizer interface defines a set of methods for creating cryptographically random numbers and strings. Implementers should be sure to use a strong cryptographic implementation, such as the JCE or BouncyCastle. Weak sources of randomness can undermine a wide variety of security mechanisms.

ESAPI's Randomizer Interface 는 다음 메서드를 정의하고 있다.


  • boolean getRandomBoolean() Returns a random boolean.
  • String getRandomFilename(String extension) Returns an unguessable random filename with the specified extension.
  • String getRandomGUID() Generates a random GUID.
  • int getRandomInteger(int min, int max) Gets the random integer.
  • long getRandomLong() Gets the random long.
  • float getRandomReal(float min, float max) Gets the random real.
  • String getRandomString(int length, char[] characterSet) Gets a random string of a desired length and character set.

ESAPI Encryptor Configuration


This requires the presence of the encryption keys in the ESAPI.properties file.
The string-based encrypt() and decrypt() methods have been deprecated in favor of the new CipherText-based methods:

ESAPI.encryptor().encrypt(PlainText plainText)
ESAPI.encryptor.decrypt(CipherText ciphertext)

다양한 랜덤 데이터 생성예:
  • ESAPI.randomizer().getRandomBoolean()
  • ESAPI.randomizer().getRandomFilename(request.getParameter("fileExtension"));
  • ESAPI.randomizer().getRandomInteger(min , max)
  • ESAPI.randomizer().getRandomReal(minFloat, maxFloat)
  • ESAPI.randomizer().getRandomString(length, charSet)
  • ESAPI.randomizer().getRandomLong()




7. Error Handling and Logging

Error handling refers to the anticipation, detection, and resolution of programming, application, and communications errors. Data logging is the process of recording events, with an automated computer program, in a certain scope in order to provide an audit trail that can be used to understand the activity of the system and to diagnose problems.


Logging

The ESAPI Logger interface defines a set of methods that can be used to log security events. The ESAPI Logger promotes secure logging functionality while allowing organizations to choose their own logging framework. The primary benefit of the ESAPI Logger is the addition of relevant security information to the log message and the use of specific tags that allow log messages to be identified as SECURITY related (as opposed to FUNCTIONAL, PERFORMANCE, etc).
The Logger interface defines a set of methods that can be used to log security events. It supports a hierarchy of logging levels which can be configured at runtime to determine the severity of events that are logged, and those below the current threshold that are discarded.

ESAPI also allows for the definition of the type of log event that is being generated. The Logger interface predefines 4 types of Log events: SECURITY_SUCCESS, SECURITY_FAILURE, EVENT_SUCCESS, EVENT_FAILURE. Implementations can extend or change this list if desired. This Logger allows callers to determine which logging levels are enabled, and to submit events at different severity levels.

Implementors of this interface should:

  • provide a mechanism for setting the logging level threshold that is currently enabled. This usually works by logging all events at and above that severity level, and discarding all events below that level. This is usually done via configuration, but can also be made accessible programmatically.
  • ensure that dangerous HTML characters are encoded before they are logged to defend against malicious injection into logs that might be viewed in an HTML based log viewer.
  • encode any CRLF characters included in log data in order to prevent log injection attacks.
  • avoid logging the user's session ID. Rather, they should log something equivalent like a generated logging session ID, or a hashed value of the session ID so they can track session specific events without risking the exposure of a live session's ID.
  • record the following information with each event: identity of the user that caused the event,a description of the event (supplied by the caller),whether the event succeeded or failed (indicated by the caller), severity level of the event (indicated by the caller), that this is a security relevant event (indicated by the caller), hostname or IP where the event occurred (and ideally the user's source IP as well),a time stamp.
  • Custom logger implementations might also filter out any sensitive data specific to the current application or organization, such as credit cards, social security numbers, etc. There are both Log4j and native Java Logging default implementations. JavaLogger uses the java.util.logging package as the basis for its logging implementation. Both default implementations implements requirements #1 thru #5 above.

Customization


It is expected that most organizations will implement their own custom Logger class in order to integrate ESAPI logging with their logging infrastructure. The ESAPI Reference Implementation is intended to provide a simple functional example of an implementation.

Configuration
There are various steps required to configure ESAPI for logging

A. Define the Log factory in the ESAPI.properties file (in your resources directory)
B. Define the Logger.* properties in ESAPI.properties

Use


The Log4JLogFactory reference implementation can be used in the following way:
//sample usage of ESAPI's Logger
Logger logger = ESAPI.getLogger("some Class or String");

logger.fatal(Logger.SECURITY_FAILURE, "some log message");
logger.debug(Logger.EVENT_FAILURE, "another log message");


Error Handling

Every application will eventually have to deal with an exception and it is vital that these are handled securely. If an attacker can force exceptions to occur and you fail to correctly handle these situations you will expose sensitive information about the inner workings of the application. These detailed error messages will help attackers build a picture of your application and fine tune their attacks.


EnterpriseSecurityException is the base class for all security related exceptions. You should pass in the root cause exception where possible. Constructors for classes extending EnterpriseSecurityException should be sure to call the appropriate super() method in order to ensure that logging and intrusion detection occur properly.


All EnterpriseSecurityExceptions have two messages, one for the user and one for the log file. This way, a message can be shown to the user that doesn't contain sensitive information or unnecessary implementation details. Meanwhile, all the critical information can be included in the exception so that it gets logged.


Note that the "logMessage" for ALL EnterpriseSecurityExceptions is logged in the log file. This feature should be used extensively throughout ESAPI implementations and the result is a fairly complete set of security log records. ALL EnterpriseSecurityExceptions are also sent to the IntrusionDetector for use in detecting anomolous patterns of application usage.




8. Data Protection

Data Protection은 컴퓨터 데이터의 오용 방지를 보장하는 프로세스이다.  ESAPI HTTPUtilities  인터페이스는 HTTP requests, responses, sessions, cookies, headers, and logging 에 대한 추가 보안을 제공하는 메소드들을 정의한다.


Prevent browser Caching

ESAPI.httpUtilities().setNoCacheHeaders(ESAPI.httpUtilities().getCurrentResponse());

 

중요한 데이터가 브라우저에 캐싱되는 것을 방지한다. 개발자는 이 메서드를 호출함으로써 중요데이터가  브라우저나 중간 프록시에 캐시되지 않도록 설정한다. 가장 안전한 방법은 가장 제한적인 설정으로 모든 관련 헤더를 설정하는 것이다.

 

다음과 같이 안전하게 헤더를 설정하도록 한다.
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: must-revalidate
Expires: -1





9. Http Security


HTTP Security는 HTTP requests, responses, sessions, cookies, headers, logging 보호를 의미한다.  ESAPI의 HTTPUtilities 인터페이스는 추가 보안을 제공하는 메서드를 정의하고 있다.


Unvalidated Redirects/Forwards

Web Application Redirects and Forwards은 사용자가 제공하는 파라미터를 목적지 URL로 사용하는 경우가 종종있다.  이런 경우 입력값에 대한 유효성 검사가 제대로 이루어지지 않는다면 공격자는 피싱또는 악성코드를 배포하는 사이트로 피해자를 보낼 수 있다. 

 ESAPI.httpUtilities().sendRedirect 는 리다이렉트에 대한 유효성을 검사하여 리다이렉트 취약점이 발생하지 않도록 한다.   ESAPI.httpUtilities().sendRedirect 는 애플리케이션의  WEB-INF 디렉토리하에 있는 리소스로만 리다이렉트를 허용한다.  유효성 검증작업은 ESAPI.properties 에서 Validation.Redirect 설정값으로 제어된다.

 




ESAPI 사용예제



Other articles in this series:
Part 0: The OWASP Top Ten and ESAPI
Part 1: The OWASP Top Ten and ESAPI – Part 1 – Cross Site Scripting (XSS)
Part 2: The OWASP Top Ten and ESAPI – Part 2 – Injection Flaws
Part 3: The OWASP Top Ten and ESAPI – Part 3 – Malicious File Execution
Part 4: The OWASP Top Ten and ESAPI – Part 4 – Insecure Direct Object Reference
Part 5: The OWASP Top Ten and ESAPI – Part 5 – Cross Site Request Forgery (CSRF)
Part 6: The OWASP Top Ten and ESAPI – Part 6 – Information Leakage and Improper Error Handling
Part 7: The OWASP Top Ten and ESAPI – Part 7 – Broken Authentication and Session Management
Part 8: The OWASP Top Ten and ESAPI – Part 8 – Insecure Cryptographic Storage
Part 9: The OWASP Top Ten and ESAPI – Part 9 – Insecure Communications
Part 10: The OWASP Top Ten and ESAPI – Part 10 – Failure to Restrict URL Access


 

ESAPI 환경설정


After installing ESAPI for Java v2 as described above, perform the following steps to prepare a project to use ESAPI:

  1. Add the ESAPI Jar to the classpath. In Project > Properties > Java Build Path > Libraries use “Add JARS…” if the ESAPI jar is part of your project directory structure (e.g., checked into source control with your project) or “Add External JARS” if you maintain a separate directory of jar dependencies.
  2. Locate ESAPI.properties and validation.properties in the configuration/.esapi directory and copy them somewhere that will be available to Run and Debug Configurations.
    • Installation Tip: A reasonable default location during development is inside a .esapi folder in your user directory.
  3. If you elected to place the ESAPI.properties and validation.properties somewhere other than your user home directory, you will need to provide the directory via a VM argument.
    • Installation Tips: In Run > Run Configuration (or Debug Configuration), on the Arguments Tab, add to VM Arguments: -Dorg.owasp.esapi.resources="/path/to/.esapi", providing the absolute or relative path of the directory containingESAPI.properties and validation.properties. To include ESAPI in all run configurations: in Preferences > Java > Installed JREs > Edit, add: -Dorg.owasp.esapi.resources="/path/to/.esapi", providing the absolute or relative path of the directory containingESAPI.properties and validation.properties.


ESAPI 2.0 Symmetric Encryption User Guide


ESAPI.properties Properties Relevant to Symmetric Encryption


출처: http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html

Those properties that are new since ESAPI 2.0-rc2 are shown in red. Values shown in blue are ones that you would replace.

Property Name

Default Value

Comment

ESAPI.Encryptor
org.owasp.esapi.reference.crypto.JavaEncryptor

The class implementing the Encryptor interface and returned by ESAPI.encryptor().

Encryptor.MasterKey

<initially unset>

The base64-encoded SecretKey. The key must be appropriate to the specified key size and cipher algorithm.

Set as per the instructions in the ESAPI Installation Guide.

Encryptor.MasterSalt

<initially unset>

A base64-encoded random “salt”. This should be at least 20-bytes. It is used to generate a random (but consistent) public/private key pair used in asymmetric encryption and digital signatures.

Set as per the instructions in the ESAPI Installation Guide.

Encryptor.EncryptionAlgorithm
AES

A deprecated property, superseded by Encryptor.CipherTransformation.

Encryptor.CipherTransformation
AES/CBC/PKCS5Padding

Specifies the cipher transformation to use for symmetric encryption. The format is cipherAlgorithm/cipherMode/paddingScheme.

Encryptor.EncryptionKeyLength
128

Key size, in bits. Required for cipher algorithms that support multiple key sizes.

Encryptor.ChooseIVMethod
random

Legal values are “random” or “fixed”. Random is recommended. Set to “fixed” only if required for compatibility with legacy or third party software. If set to “fixed”, then the property Encryptor.fixedIV must also be set to hex-encoded specific IV that you need to use.

CAUTION: While it is not required that the IV be kept secret, encryption relying on fixed IVs can lead to a known plaintext attack called a "Key Collision Attack". While this attack is probably not practical (for those with modest resources) for ciphers with 128-bit key size, this attack makes it possible to capture the ciphertexts from only 2(N/2) known plaintexts to discover the encryption key. Loughran and Dowling explain a Java implementation of Eli Biham's key collision attack on DES in their easy to understand paper, A Java Implemented Key Collision Attack on the Data Encryption Standard (DES). Since attacks only get better and the cost of storage is dropping rapidly, you are urged to avoid using "fixed" IVs except when required for backward compatibility. In particular, should never use fixed IVs just to avoid the storage cost of storing a random IV.

Encryptor.fixedIV
0x000102030405060708090a0b0c0d0e0f

A hex-encoded value to use as a fixed IV. Only used if the property Encryptor.fixedIV is set to “fixed”. Intended only for compatibility with legacy code. See the above related above caution forEncryptor.ChooseIVMethod.

Encryptor.CipherText.useMAC
true

Whether or not CipherText should use a message authentication code (MAC) with it. This prevents an adversary from altering the IV as well as allowing a more fool-proof way of determining the decryption failed because of an incorrect key being supplied. This refers to the "separate" MAC calculated and stored in CipherText, not part of any MAC that is calculated as a result of a "combined mode" cipher mode.

Note: If the cipher mode used is one specified in the comma-separated list of cipher modes given in the propertyEncryptor.cipher_modes.combined_modes, then a separate MAC is not calculated forCipherText regardless of the setting of this property. (Doing so would be superfluous.)

Encryptor.PreferredJCEProvider
<empty string>

Specifies the preferred JCE provider, that is the JCE provider that is first looked at for JCE algorithms. The Encryptor reference implementation, JavaEncryptor, attempts to load this JCE provider at position the first position when theJavaEncryptor class is first loaded. 

The value may either be a provider name (e.g., “BC” for Bouncy Castle) or the fully qualified class name implementing java.security.Provider for the desired JCE provider. If left set to the empty string (the default) or unset, the effect is to not change the preferred JCE provider so that your application ends up using whatever your Java VM is already using, which is generally determined by the settings in your$JAVA_HOME/jre/lib/security/java.security file.

Encryptor.cipher_modes.additional_allowed
CBC

Additional cipher modes allowed for ESAPI 2.0 symmetric encryption. These cipher modes are in addition to those specified by the propertyEncryptor.cipher_modes.combined_modes.

Note: We will add support for streaming modes like CFB & OFB once we add support for 'specified' to the propertyEncryptor.ChooseIVMethod (probably in ESAPI 2.1).

Encryptor.cipher_modes.combined_modes
GCM,CCM,IAPM,EAX,OCB,CWC

Comma-separated list of cipher modes that provide both confidentiality and message authenticity. (NIST refers to such cipher modes as "combined modes" so that's what we shall call them.) If any of these cipher modes are used then no MAC is calculated and stored in theCipherText upon encryption. Likewise, if one of these cipher modes is used with decryption, no attempt will be made to validate the MAC contained in the CipherText object regardless of whether it contains one or not. Since the expectation is that these cipher modes support support message authenticity already, injecting a MAC in the CipherText object would be at best redundant.

Note that as of JDK 1.5, the SunJCE provider does not support any of these cipher modes. Of these listed, only GCM and CCM are currently NIST approved.

Encryptor.PlainText.overwrite
TRUE

Whether or not the plaintext bytes for the PlainText object may be overwritten with “*” characters and then marked eligible for garbage collection. If not set, this is still treated as 'true'. If this is set to 'true', you will not be able to use any PlainText object after you have used it with one of the Encryptor encrypt() methods.

Encryptor.KDF.PDF
HmacSHA256

This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function (KDF) normally uses. NSA wanted us to support something stronger than HmacSHA1 (even though that is considered fine for now--unless perhaps you are guarding nuclear launch codes--and if so, your Java license probably prohibits that ;-). 

(Note this is *only* the PRF used for ESAPI's KDF and *not* what is used for ESAPI's MAC. Currently, HMacSHA1 is always used for the MAC.) 

Currently supported choices for JDK 1.5 and 1.6 are: HMacSHA1 (160 bits), HMacSHA256 (256 bits), HMacSHA384 (384 bits), and HMacSHA512 (512 bits). 

Note that HMacMD5 is *not* supported for the PRF used by the KDF even though these JDKs support it. ESAPI 2.0 release candidates prior to 2.0_rc11 ALWAYS used HMacSHA1. It is somewhat faster than the SHA2 HMAC variations, but not significantly so. 

The only legitimate reason to tweak this would be to change it to one of the new HMACs based on the future SHA-3 winner once it is announced by NIST and supported in ESAPI and most JDKs. Until then, don't meddle.

Encryptor.CharacterEncoding
UTF-8

The default encoding used for certain aspects such as signing and sealing.

How the Old (Deprecated) Methods Were Used

To encrypt / decrypt using the String-based, deprecated methods carried over from ESAPI 1.4, code similar to the following would be used.

    String myplaintext = "My plaintext";
    try {
        String ciphertext = ESAPI.encryptor().encrypt(myplaintext);
        String decrypted  = ESAPI.encryptor().decrypt(ciphertext);
        assert decrypted.equals(myplaintext);
    } catch(EncryptionException ex) {
        // Log error then return error designation however appropriate
    }

This code will still work, however if you are using the standard (default) reference for ESAPI.Encryptor, which is org.owasp.esapi.reference.crypto.JavaEncryptor, the cipher transformation used with be that specified by the property Encryptor.CipherTransformation with a key size (when the algorithm supports a variable key size) of that specified by Encryptor.EncryptionKeyLength and the IV type specified by Encryptor.ChooseIVMethod. What is not provided by these methods (and why they are deprecated) is that they provide no mechanism to ensure message authenticity unless they are used with a so-called “combined” cipher mode such as CCM or GCM. (Note that as of JDK 1.6, the default JCE provider, “SunJCE”, does not support any combined cipher modes.)

Requirements for using ESAPI Symmetric Encryption

The parties participating in using ESAPI's symmetric encryption capabilities must agree on the following:

  1. All parties must share the same encryption key(s). If multiple keys are used, each party must know which key to use for encryption / decryption. Parties must ensure that exchanging, storing, and all management of these encryption keys is done securely. How this is done is presently outside the scope of ESAPI encryptions, but developers may find the advice in OWASP's Cryptographic Storage Cheat Sheet helpful in this regard.
  2. If parties are only exchanging raw ciphertext (plus IV, when appropriate), then they must also agree on the cipher transformation to use.
  3. If the involved parties participating in encryption are not using a "combined" cipher mode that provides both confidentiality and authenticity (for example, something like GCM or CCM), then they mustwhether or not there will be an additional MAC sent allow with the raw ciphertext and other cipher spec information in order to provide evidence of authenticity / data integrity. (See the propertyEncryptor.CipherText.useMAC for further details.) Failure to agree on this may allow an adversary to carry out certain chosen ciphertext attacks against their encrypted data resulting in (possibly complete) leakage of the corresponding plaintext.

Encrypting / Decrypting with the New Methods -- The Simple Usage

Using the new encryption / decryption methods is somewhat more complicated, but this is in part because they are more flexible and that flexibility means that more information needs to be communicated as to the details of the encryption.

A code snippet using the new methods that use the master encryption key would look something like this:

    String myplaintext = "My plaintext";
    try {
        CipherText ciphertext =
            ESAPI.encryptor().encrypt( new PlainText(myplaintext) );
        PlainText recoveredPlaintext = ESAPI.encryptor().decrypt(ciphertext) );
        assert myplaintext.equals( recoveredPlaintext.toString() );
    } catch(EncryptionException ex) {
        // Log error then return error designation however appropriate.
    }

Yes, this is a bit more complicated, but it will 1) work across different hardware platforms and operating systems whereas the older methods may not, and 2) it provides for authenticity and confidentiality of the ciphertext regardless of which cipher mode is chosen.

Also, these new methods allow a general byte array to be encrypted, not just a Java String. If one needed to encrypt a byte array with the old deprecated method, one would first have to use

    byte[] plaintextByteArray = { /* byte array to be encrypted */ };
    String plaintext = new String(plaintextByteArray, "UTF-8");

all the while catching the required UnsupportedEncodingException. For example, to handle this in ESAPI 1.4, one would have to write something like:

    try {
        byte[] plaintextByteArray = { /* byte array to be encrypted */ };
        String myplaintext = new String(plaintextByteArray, "UTF-8");
        String ciphertext = ESAPI.encryptor().encrypt(myplaintext);
        String decrypted  = ESAPI.encryptor().decrypt(ciphertext);
        byte[] recoveredBytes = decrypted.getBytes(“UFT-8”);
        assert java.util.Arrays.equals( plaintextByteArray, recoveredBytes );
    } catch( UnsupportedEncodingException ex) {
        // Should not happen but need to catch and deal with it anyhow.
        // Log error then return error designation however appropriate.
    } catch(EncryptionException ex) {
        // Log error then return error designation however appropriate.
    }

However, dealing with this in ESAPI 2.0 is not any more cumbersome than dealing with Strings:

    try {
        byte[] plaintextByteArray = { /* byte array to be encrypted */ };
        CipherText ciphertext =
            ESAPI.encryptor().encrypt( new PlainText(plaintextByteArray) );
        PlainText recoveredPlaintext = ESAPI.encryptor().decrypt(ciphertext) );

        assert java.util.Arrays.equals( plaintextByteArray,
                                        recoveredPlaintext.asBytes() );
    } catch(EncryptionException ex) {
        // Log error then return error designation however appropriate.
    }

Ideally when you are encrypting sensitive data you do not want the plaintext sensitive data to be left lying around after it is encrypted. Instead, you should overwrite them after their value as been used. However, when you are using immutable Strings, this is not possible using native Java methods. But if you are able to pass in byte arrays that are passed directly to PlainText objects (as shown above), thedefault is to overwrite this after they are encrypted. (Note: Verify!) If the default for Encryptor.PlainText.overwrite of true had been used, then the array plaintextByteArray would have been overwritten with ASCII “*” characters.

Encrypting / Decrypting with the New Methods – Storing Encrypted Data

If you use one of the new Encryptor encrypt() / decrypt() methods, how do you persist the CipherText object returned by the encrypt() methods and how do you restore it to pass to the decrypt() method?

The following example code snippet will illustrate this. In the following example we will simply write out the serialized CipherText object to a local file, but obviously you could hex- or base64-encode the serialized byte array and store it in a database or sent it in a SOAP XML message to a web service, etc.

    public class PersistedEncryptedData
    {
        public static int persistEncryptedData(PlainText plaintext,
                                                String filename)
            throws EncryptionException, IOException
        {
            File serializedFile = new File(filename);
            serializedFile.delete(); // Delete any old serialized file.

            CipherText ct = ESAPI.encryptor().encrypt(plaintext);
            byte[] serializedCiphertext = ct.asPortableSerializedByteArray();

            FileOutputStream fos = new FileOutputStream(serializedFile);
            fos.write(serializedCiphertext);
            fos.close();
            return serializedCiphertext.length;
        }

        public static PlainText restorePlaintext(String encryptedDataFilename)
            throws EncryptionException, IOException
        {
            File serializedFile = new File(encryptedDataFilename);
            FileInputStream fis = new FileInputStream(serializedFile);
            int avail = fis.available();
            byte[] bytes = new byte[avail];
            fis.read(bytes, 0, avail);

            CipherText restoredCipherText =
                                CipherText.fromPortableSerializedBytes(bytes);
            fis.close();
            PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
            return plaintext;
        }
    }
  

Advanced Usage

Encrypting / Decrypting with the New Methods

ESAPI 1.4 and earlier only allowed you to use the master key (MasterPassword in ESAPI 1.4; Encryptor.MasterKey in ESAPI 2.0) to encrypt and decrypt with. But encryption with a single key seldom is sufficient. For instance, lets say that your application has a need to encrypt both bank account numbers and credit card numbers. The encrypted bank account numbers are to be sent to one recipient and the encrypted credit card numbers are to be sent to a different recipient. Obviously in such cases, you do not want to share the same key for both recipients.

In ESAPI 1.4 there was not much you can do, but in ESAPI 2.0 and later, there are new encryption / decryption methods that allow you to specify a specific SecretKey. There is also a static helper method inCryptoHelper to allow you to generate a SecretKey of a specific type. (Distribution of this key is out of scope for this particular example, but for the moment, we will assume that secret keys are first generated, and then distributed to the recipients out-of-band. On you could distribute them dynamically via asymmetric encryption assuming that you've previously exchanged public keys with the recipients.)

The following illustrates how these new methods might be used.

First, we would generate some appropriate secret keys and distribute them securely (e.g., perhaps over SSL/TLS) or exchange them earlier out-of-band to the intended recipients. (E.g., one could put them on two separate thumb drives and use a trusted courier to distribute them to the recipients or one could use PGP-mail or S/MIME to securely email them, etc.)

    // Generate two random, 128-bit AES keys to be distributed out-of-band.
    import javax.crypto.SecretKey;
    import org.owasp.esapi.crypto.CryptoHelper;
    import org.owasp.esapi.codecs.Hex;

    public class MySecretKeys {
        public void main(String[] args) {
          try {
            SecretKey bankAcctKey = CryptoHelper.generateSecretKey("AES", 128);
            SecretKey credCardKey = CryptoHelper.generateSecretKey("AES", 128);

            System.out.println("Bank account key: " +
                Hex.encode( bankAcctKey.getEncoding(), true ) );
            System.out.println("Credit card key: " +
                Hex.encode( credCardKey.getEncoding(), true ) );
          } catch(Exception ex) {
            ex.printStackTrace(System.err);
            System.exit(1);
          }
          System.exit(0);
        }
    }

Second, these keys would be printed out and stored somewhere secure by our application, perhaps using something like ESAPI's EncryptedProperties class, where they could later be retrieved and used.

In the following code, we assume that the SecretKey values have already been initialized elsewhere.

    SecretKey bankAcctKey = ...;        // These might be read from EncryptedProperties
    SecretKey credCardKey = ...;        // or from a restricted database, etc.
    ...
    String bankAccountNumber = ...;     // Assume obtained elsewhere
    String creditCardNumber = ...;      // Ditto
    ...
    try {
        // Encrypt each with their appropriate secret key
        CipherText encryptedBankAcct =
            ESAPI.encryptor().encrypt( bankAcctKey, new PlainText(bankAccountNumber) );
        CipherText encryptedCreditCard =
            ESAPI.encryptor().encrypt( credCardKey, new PlainText(creditCardNumber) );
        ...
        // Decrypt using appropriate secret key
        PlainText recoveredBankAcct = ESAPI.encryptor().decrypt( bankAcctKey, encryptedBankAcct ) );
        assert bankAccountNumber.equals( recoveredBankAcct );
        ... etc. ...
    } catch(EncryptionException ex) {
        // Log error then return error designation however appropriate.
    }

Using ESAPI with Multiple Cipher Transformations

For the most part, the architecture of ESAPI assumes that you will stick to using the defaults in the ESAPI.properties configuration file or implment your own classes--possibly by extending the ESAPI reference classes--and use these classes instead of the reference classes.

For most things, this works well. Most applications likely can standardize on a single cipher transformation such as AES/CBC/PKCS5Padding with a 128-bit AES key and use that 100% of the time. However, on occassion, an application may need to use two separate cipher transformations (or even two different cipher algorithms) to handle legacy applications or deal with multiple partners.

This section discusses how to do this without implementing your own classes or extending the ESAPI reference class, JavaEncryptor. Note that it is recognized that this approach is somewhat of a kludge. A simpler approach is planned for ESAPI 2.1, but the approach shown here is workable even though it's not pretty.

If you find yourself in need of encrypting with a different cipher transformation, the first thing that you should count on is not using the same encryption key for each. While in some cases this likely would work (e.g., you are only using a different cipher mode or you have 256-bit AES key for "Encryptor.MasterKey" but also have a need to do encryption with a 128-bit AES key), it is not guaranteed to do so. Instead, you should count on generating a separate encryption key and using the encrypt / decrypt methods taking an additional SecretKey parameter as show in the previous section.

Rather than repeating all the details of how to do this in this user guide, we encourage you to investigate how this was done in the Junit testing for the JavaEncryptor class. Please look at the source code for the private method runNewEncryptDecryptTestCase(String, int, byte[]) in the EncryptorTest JUnit test in the source code "src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java". This code calls:

		ESAPI.securityConfiguration().setCipherTransformation(cipherXform);

which sets ESAPI to use the specified cipher transformation, cipherXform. As a convenience (for later restoral), it returns the previous cipher transformation.

There are also a few non-obvious key size adjustments that are also going on with DES and DESede (aka, triple DES) keys that are made there as well. This has to do with the fact that for DES keys (which includes DESede), the "true" key size differs from the "effective" key size. (E.g., in DES, the "true" key size--from the DES algorithms's perspective is 64-bits, however the effective key size for DES is only 56-bits because of the 8-bits of parity "imposed" by the NSA in the early 1970s.) This inconsistency manifests itself in the JCE by the fact that KeyGenerator.init() wants the effective key size to be specified (so 56-bits for DES, 112- or 168-bits for DESede), but SecretKey.getEncoding().length stores the "true" key size (e.g., 64-bits or 192-bits for DES and DESede respectively). Other cipher algorithms do not have this discrepancy between true and effective key sizes. Since a SecretKey is returned by the KeyGenerator, this is only all too confusing. The reference JavaEncryptor and its JUnit test, EncryptorTest attempt to deal with this discrepency. See the adjustments made to key size in EncryptorTest.runNewEncryptDecryptTestCase() for further details.

Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules

If you wish to use ESAPI with a FIPS 140-2 validated, JCE-compliant cryptographic module such as the IBM Java JCE FIPS 140-2 Cryptographic Module (hereafter referred to as IBMJCEFIPS) or the RSA Security BSafe Crypto-J (hereafter referred to as Crypto-J), or other similar products, you must follow these instructions. Failure to do so will mean that you will fail to meet FISMA compliance. (Note: The mention of these two specific vendor products does not constitute an endorsement from either OWASP or this author.) 

  1. Follow the vendor's regular instructions to configure your application to use the vendor product in a FIPS 140-2 compliant manner. Generally this will include some special configuration and/or initialization requirements that must be followed to restrict the vendor software to use FIPS 140-2 approved algorithms. 

    If the vendor's instructions do not already include a recommendation to edit the $JAVA_HOME/jre/lib/security/java.security file, the OWASP team recommends that you do so to prevent you from accidentally using any other cryptographic module (such as SunJCE). To do this, you will need to change the property "security.provider.1" (which represents the default) provider so that it refers to the fully-qualified classname of your vendor's JCE Provider class. (By default, this is set to "sun.security.provider.Sun".) In fact, you may wish to go as far as to either comment out or remove the other providers to reduce the possibility that they will accidentally be used by your application.
  2. Edit the ESAPI.properties that your application is going to be using and set the property "Encryptor.PreferredJCEProvider" to the fully qualified classname of your vendor's FIPS 140-2 compliant JCE provider class. (If you are using a Java SecurityManager you may also need to grant org.owasp.esapi.reference.crypto.JavaEncryptor permissions to change the JCE provider.)
  3. Edit the ESAPI.properties that your application is going to be using and set the property Encryptor.CipherText.useMAC to false. This is critical as having this property set to true causes ESAPI to use derived keys for the actual encryption and MAC calculation, which is against FIPS 140-2 compliance. (Note: This does not mean that OWASP believes that this key derivation is weak--its design has been suggested some cryptographers--but it would be creating and using a related key for encryption in a manner not reviewed by NIST and thus it is not acceptable to FIPS 140-2 approval.)
  4. Edit the ESAPI.properties that your application is going to be using and set the property "Encryptor.CipherTransformation" to a cipher transformation that is FIPS 140-2 certified for you vendor's software. OWASP recommends using a NIST "combined" cipher mode, that is one that provides for both message confidentiality and message authenticity if such a cipher mode is available from your vendor and FIPS 140-2 certified. (Cipher modes "GCM" and "CCM" are the only FIPS 140-2 approved as of this writing.) If such a "combined" cipher mode is not available, then use a cipher transformation like "AES/CBC/PKCS5Padding" with a random IV. This is almost always available as one of the FIPS 140-2 certified cipher transformations, however it will not provide you with message authenticity because to remain FIPS 140-2 compliant you will have had to disable the MAC calculation (via setting Encryptor.CipherText.useMAC to false) causing ESAPI to skip calculating an explicit MAC for you and hence providing no assurance of data integrity to the party attempting to decrypt your data. Without authenticity however, your encryption may still be vulnerable to the "padded oracle" chose ciphertext attack. Consult with your local cryptographic expert in such cases as this depends greatly on the circumstances of how you are using encryption.
  5. If your vendor softare requires you to explicitly to initialize their software for FIPS-mode (e.g., to cause the required FIPS 140-2 "power-on self tests" to run) by calling some software method, then this must be done by your application code before calling any of the ESAPI crypto-related code. Specifically, it must be called before your application calls ESAPI.encryptor().


We believe that following these steps will still allow for your application to be FIPS 140-2 / FISMA compliant, however, as always, you should check with your FIPS auditor before using ESAPI in this manner. Should your auditors ask, feel free to point them to the ESAPI source code, which we believe will convince them that ESAPI is not a cryptographic module. (Rather, it only provides a wrapper to call other cryptographic modules through the standard JCE APIs.)

Acknowledgments

The OWASP ESAPI development team would like to acknowledge the contributions of cryptographers David A. Wagner and Ian Grigg. David provided the outline of how to securely compute derived keys for confidentiality and authenticity from a single master key and Ian convinced us to take a packetizing approach to laying out the portable, serialized CipherText object.

In addition, Kevin Kenan has agreed to review all the crypto code from ESAPI 2.0-rc5 or 2.0-rc6 candidate release. Others are invited to participate as well, especially those with a background in cryptography. (See "Request to review ESAPI 2.0 crypto" for details.)

I would also like to thank Jessica Fitzgerald-McKay and Andy Sampson of the Systems and Network Analysis Center of the NSA for their excellent feedback in response to the review request made in Google Issue #81. Working with bureaucracy is not something that I do particularly well, but Jessica and Andy made it as painless as it possibly could be. Their feedback, which at this point, unfortunately we are unable to quote, was generally favorable, but in the places where they had recommendations, these were very precise and helpful.

Lastly, I would like to thank Jeffrey Walton of Software Integrity, LLC. Jeff provided an extremely thorough analysis of ESAPI 2.0's (as of 2.0_rc10) Key Derivation Function (KDF). Jeff's analysis convinced me to bring ESAPI's KDF more in line with NIST's recommendations for KDFs as described in NIST Special Publication 800-108 (and specifically section 5.1). You can read about Jeff's review at Analysis of ESAPI 2.0's Key Derivation Function