HTML5 Security Cheat Sheet
|
Last revision (mm/dd/yy): 02/25/2018 Introduction
The following cheat sheet serves as a guide for implementing HTML 5 in a secure fashion. Communication APIsWeb MessagingWeb Messaging (also known as Cross Domain Messaging) provides a means of messaging between documents from different origins in a way that is generally safer than the multiple hacks used in the past to accomplish this task. However, there are still some recommendations to keep in mind:
Cross Origin Resource Sharing
WebSockets
Server-Sent Events
Storage APIsLocal Storage
Client-side databases
Geolocation
Web Workers
TabnabbingAttack is described in details into this article.
function openPopup(url, name, windowFeatures){
//Open the popup and set the opener and referrer policy instruction
var newWindow = window.open(url, name, 'noopener,noreferrer,' + windowFeatures);
//Reset the opener link
newWindow.opener = null;
}
Sandboxed frames
It is possible to have a fine-grained control over iframe capabilities using the value of the sandbox attribute.
Offline Applications
Progressive Enhancements and Graceful Degradation Risks
HTTP Headers to enhance securityX-Frame-Options
X-XSS-Protection
Strict Transport Security
Content Security Policy
Origin
WebSocket implementation hintsIn addition to the elements mentioned above, this is the list of area for which caution must be taken during the implementation.
The section below will propose some implementation hints on every area and will be along with an application example showing all the points described. The complete source code of the example application is available here. Access filteringDuring a websocket channel initiation, the browser send the Origin HTTP request header that contain th source domain initiation the request to handshake. Event if this header can be spoofed in a forged HTTP request (not browser based), it cannot be overrided or forced in a browser context. It then represent a good candidate to apply filtering according to an expected value. An example of attack using this vector and named Cross-Site WebSocket Hijacking (CSWSH) is described here. The code below define a configuration that apply filtering based on a "whitelist" of origins. This ensure that only allowed origins can establish a full handshake: import org.owasp.encoder.Encode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.server.ServerEndpointConfig;
import java.util.Arrays;
import java.util.List;
/**
* Setup handshake rules applied to all WebSocket endpoints of the application.
* Use to setup the Access Filtering using "Origin" HTTP header as input information.
*
* @see "http://docs.oracle.com/javaee/7/api/index.html?javax/websocket/server/ServerEndpointConfig.Configurator.html"
* @see "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin"
*/
public class EndpointConfigurator extends ServerEndpointConfig.Configurator {
/**
* Logger
*/
private static final Logger LOG = LoggerFactory.getLogger(EndpointConfigurator.class);
/**
* Get the expected source origins from a JVM property in order to allow external configuration
*/
private static final List<String> EXPECTED_ORIGINS = Arrays.asList(System.getProperty("source.origins").split(";"));
/**
* {@inheritDoc}
*/
@Override
public boolean checkOrigin(String originHeaderValue) {
boolean isAllowed = EXPECTED_ORIGINS.contains(originHeaderValue);
String safeOriginValue = Encode.forHtmlContent(originHeaderValue);
if (isAllowed) {
LOG.info("[EndpointConfigurator] New handshake request received from {} and was accepted.", safeOriginValue);
} else {
LOG.warn("[EndpointConfigurator] New handshake request received from {} and was rejected !", safeOriginValue);
}
return isAllowed;
}
}
Authentication and Input/Output validationWhen using websocket as communication channel, it's important to use an authentication method allowing the user to receive an access Token that is not automatically sent by the browser and then must be expliclty sent by the client code during each exchange. JSON Web Token is a good candidate because it allow to transport access ticket information in a stateless and not alterable way. Moreover, it define a validity timeframe. You can find additional information about JWT token hardening on this article. JSON Validation Schema are used to define and validate the expected content in input and ouput messages. The code below define the complete authentication messages flow handling: Authentication Web Socket endpoint - Provide a WS endpoint the enable authentication exchange import org.owasp.pocwebsocket.configurator.EndpointConfigurator;
import org.owasp.pocwebsocket.decoder.AuthenticationRequestDecoder;
import org.owasp.pocwebsocket.encoder.AuthenticationResponseEncoder;
import org.owasp.pocwebsocket.handler.AuthenticationMessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
/**
* Class in charge of managing the client authentication.
*
* @see "http://docs.oracle.com/javaee/7/api/javax/websocket/server/ServerEndpointConfig.Configurator.html"
* @see "http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/"
*/
@ServerEndpoint(value = "/auth", configurator = EndpointConfigurator.class, subprotocols = {"authentication"}, encoders = {AuthenticationResponseEncoder.class}, decoders = {AuthenticationRequestDecoder.class})
public class AuthenticationEndpoint {
/**
* Logger
*/
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationEndpoint.class);
/**
* Handle the beginning of an exchange
*
* @param session Exchange session information
*/
@OnOpen
public void start(Session session) {
//Define connection idle timeout and message limits in order to mitigate as much as possible DOS attacks using massive connection opening or massive big messages sending
int msgMaxSize = 1024 * 1024;//1 MB
session.setMaxIdleTimeout(60000);//1 minute
session.setMaxTextMessageBufferSize(msgMaxSize);
session.setMaxBinaryMessageBufferSize(msgMaxSize);
//Log exchange start
LOG.info("[AuthenticationEndpoint] Session {} started", session.getId());
//Affect a new message handler instance in order to process the exchange
session.addMessageHandler(new AuthenticationMessageHandler(session.getBasicRemote()));
LOG.info("[AuthenticationEndpoint] Session {} message handler affected for processing", session.getId());
}
/**
* Handle error case
*
* @param session Exchange session information
* @param thr Error details
*/
@OnError
public void onError(Session session, Throwable thr) {
LOG.error("[AuthenticationEndpoint] Error occur in session {}", session.getId(), thr);
}
/**
* Handle close event
*
* @param session Exchange session information
* @param closeReason Exchange closing reason
*/
@OnClose
public void onClose(Session session, CloseReason closeReason) {
LOG.info("[AuthenticationEndpoint] Session {} closed: {}", session.getId(), closeReason.getReasonPhrase());
}
}
import org.owasp.pocwebsocket.enumeration.AccessLevel;
import org.owasp.pocwebsocket.util.AuthenticationUtils;
import org.owasp.pocwebsocket.vo.AuthenticationRequest;
import org.owasp.pocwebsocket.vo.AuthenticationResponse;
import org.owasp.encoder.Encode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.EncodeException;
import javax.websocket.MessageHandler;
import javax.websocket.RemoteEndpoint;
import java.io.IOException;
/**
* Handle authentication message flow
*/
public class AuthenticationMessageHandler implements MessageHandler.Whole<AuthenticationRequest> {
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationMessageHandler.class);
/**
* Reference to the communication channel with the client
*/
private RemoteEndpoint.Basic clientConnection;
/**
* Constructor
*
* @param clientConnection Reference to the communication channel with the client
*/
public AuthenticationMessageHandler(RemoteEndpoint.Basic clientConnection) {
this.clientConnection = clientConnection;
}
/**
* {@inheritDoc}
*/
@Override
public void onMessage(AuthenticationRequest message) {
AuthenticationResponse response = null;
try {
//Authenticate
String authenticationToken = "";
String accessLevel = this.authenticate(message.getLogin(), message.getPassword());
if (accessLevel != null) {
//Create a simple JSON token representing the authentication profile
authenticationToken = AuthenticationUtils.issueToken(message.getLogin(), accessLevel);
}
//Build the response object
String safeLoginValue = Encode.forHtmlContent(message.getLogin());
if (!authenticationToken.isEmpty()) {
response = new AuthenticationResponse(true, authenticationToken, "Authentication succeed !");
LOG.info("[AuthenticationMessageHandler] User {} authentication succeed.", safeLoginValue);
} else {
response = new AuthenticationResponse(false, authenticationToken, "Authentication failed !");
LOG.warn("[AuthenticationMessageHandler] User {} authentication failed.", safeLoginValue);
}
} catch (Exception e) {
LOG.error("[AuthenticationMessageHandler] Error occur in authentication process.", e);
//Build the response object indicating that authentication fail
response = new AuthenticationResponse(false, "", "Authentication failed !");
} finally {
//Send response
try {
this.clientConnection.sendObject(response);
} catch (IOException | EncodeException e) {
LOG.error("[AuthenticationMessageHandler] Error occur in response object sending.", e);
}
}
}
/**
* Authenticate the user
*
* @param login User login
* @param password User password
* @return The access level if the authentication succeed or NULL if the authentication failed
*/
private String authenticate(String login, String password) {
....
}
}
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Calendar;
import java.util.Locale;
/**
* Utility class to manage the authentication JWT token
*/
public class AuthenticationUtils {
/**
* Build a JWT token for a user
*
* @param login User login
* @param accessLevel Access level of the user
* @return The Base64 encoded JWT token
* @throws Exception If any error occur during the issuing
*/
public static String issueToken(String login, String accessLevel) throws Exception {
//Issue a JWT token with validity of 30 minutes
Algorithm algorithm = Algorithm.HMAC256(loadSecret());
Calendar c = Calendar.getInstance();
c.add(Calendar.MINUTE, 30);
return JWT.create().withIssuer("WEBSOCKET-SERVER").withSubject(login).withExpiresAt(c.getTime()).withClaim("access_level", accessLevel.trim().toUpperCase(Locale.US)).sign(algorithm);
}
/**
* Verify the validity of the provided JWT token
*
* @param token JWT token encoded to verify
* @return The verified and decoded token with user authentication and authorization (access level) information
* @throws Exception If any error occur during the token validation
*/
public static DecodedJWT validateToken(String token) throws Exception {
Algorithm algorithm = Algorithm.HMAC256(loadSecret());
JWTVerifier verifier = JWT.require(algorithm).withIssuer("WEBSOCKET-SERVER").build();
return verifier.verify(token);
}
/**
* Load the JWT secret used to sign token using a byte array for secret storage in order to avoid persistent string in memory
*
* @return The secret as byte array
* @throws IOException If any error occur during the secret loading
*/
private static byte[] loadSecret() throws IOException {
return Files.readAllBytes(Paths.get("src", "main", "resources", "jwt-secret.txt"));
}
}
{ "$schema": "http://json-schema.org/schema#", "title": "AuthenticationRequest", "type": "object", "properties": { "login": { "type": "string", "pattern": "^[a-zA-Z]{1,10}$" }, "password": { "type": "string" } }, "required": [ "login", "password" ] } { "$schema": "http://json-schema.org/schema#", "title": "AuthenticationResponse", "type": "object", "properties": { "isSuccess;": { "type": "boolean" }, "token": { "type": "string", "pattern": "^[a-zA-Z0-9+/=\\._-]{0,500}$" }, "message": { "type": "string", "pattern": "^[a-zA-Z0-9!\\s]{0,100}$" } }, "required": [ "isSuccess", "token", "message" ] }
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.google.gson.Gson;
import org.owasp.pocwebsocket.vo.AuthenticationRequest;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import java.io.File;
import java.io.IOException;
/**
* Decode JSON text representation to an AuthenticationRequest object
*

* As there one instance of the decoder class by endpoint session so we can use the JsonSchema as decoder instance variable.
*/
public class AuthenticationRequestDecoder implements Decoder.Text<AuthenticationRequest> {
/**
* JSON validation schema associated to this type of message
*/
private JsonSchema validationSchema = null;
/**
* Initialize decoder and associated JSON validation schema
*
* @throws IOException If any error occur during the object creation
* @throws ProcessingException If any error occur during the schema loading
*/
public AuthenticationRequestDecoder() throws IOException, ProcessingException {
JsonNode node = JsonLoader.fromFile(new File("src/main/resources/authentication-request-schema.json"));
this.validationSchema = JsonSchemaFactory.byDefault().getJsonSchema(node);
}
/**
* {@inheritDoc}
*/
@Override
public AuthenticationRequest decode(String s) throws DecodeException {
try {
//Validate the provided representation against the dedicated schema
//Use validation mode with report in order to enable further inspection/tracing of the error details
//Moreover the validation method "validInstance()" generate a NullPointerException if the representation do not respect the expected schema
//so it's more proper to use the validation method with report
ProcessingReport validationReport = this.validationSchema.validate(JsonLoader.fromString(s), true);
//Ensure there no error
if (!validationReport.isSuccess()) {
//Simply reject the message here: Don't care about error details...
throw new DecodeException(s, "Validation of the provided representation failed !");
}
} catch (IOException | ProcessingException e) {
throw new DecodeException(s, "Cannot validate the provided representation to a JSON valid representation !", e);
}
return new Gson().fromJson(s, AuthenticationRequest.class);
}
/**
* {@inheritDoc}
*/
@Override
public boolean willDecode(String s) {
boolean canDecode = false;
//If the provided JSON representation is empty/null then we indicate that representation cannot be decoded to our expected object
if (s == null || s.trim().isEmpty()) {
return canDecode;
}
//Try to cast the provided JSON representation to our object to validate at least the structure (content validation is done during decoding)
try {
AuthenticationRequest test = new Gson().fromJson(s, AuthenticationRequest.class);
canDecode = (test != null);
} catch (Exception e) {
//Ignore explicitly any casting error...
}
return canDecode;
}
/**
* {@inheritDoc}
*/
@Override
public void init(EndpointConfig config) {
//Not used
}
/**
* {@inheritDoc}
*/
@Override
public void destroy() {
//Not used
}
}
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.google.gson.Gson;
import org.owasp.pocwebsocket.vo.AuthenticationResponse;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import java.io.File;
import java.io.IOException;
/**
* Encode AuthenticationResponse object to JSON text representation.
*

* As there one instance of the encoder class by endpoint session so we can use the JsonSchema as encoder instance variable.
*/
public class AuthenticationResponseEncoder implements Encoder.Text<AuthenticationResponse> {
/**
* JSON validation schema associated to this type of message
*/
private JsonSchema validationSchema = null;
/**
* Initialize encoder and associated JSON validation schema
*
* @throws IOException If any error occur during the object creation
* @throws ProcessingException If any error occur during the schema loading
*/
public AuthenticationResponseEncoder() throws IOException, ProcessingException {
JsonNode node = JsonLoader.fromFile(new File("src/main/resources/authentication-response-schema.json"));
this.validationSchema = JsonSchemaFactory.byDefault().getJsonSchema(node);
}
/**
* {@inheritDoc}
*/
@Override
public String encode(AuthenticationResponse object) throws EncodeException {
//Generate the JSON representation
String json = new Gson().toJson(object);
try {
//Validate the generated representation against the dedicated schema
//Use validation mode with report in order to enable further inspection/tracing of the error details
//Moreover the validation method "validInstance()" generate a NullPointerException if the representation do not respect the expected schema
//so it's more proper to use the validation method with report
ProcessingReport validationReport = this.validationSchema.validate(JsonLoader.fromString(json), true);
//Ensure there no error
if (!validationReport.isSuccess()) {
//Simply reject the message here: Don't care about error details...
throw new EncodeException(object, "Validation of the generated representation failed !");
}
} catch (IOException | ProcessingException e) {
throw new EncodeException(object, "Cannot validate the generated representation to a JSON valid representation !", e);
}
return json;
}
/**
* {@inheritDoc}
*/
@Override
public void init(EndpointConfig config) {
//Not used
}
/**
* {@inheritDoc}
*/
@Override
public void destroy() {
//Not used
}
}
Note that the same approach is used in the messages handling part of the POC. All messages exchanged between the client and the server are systematically validated using the same way, using dedicated JSON schemas linked to messages dedicated Encoder/Decoder (serialization/deserialization). Authorization and access token explicit invalidationAuthorization information is stored in the access token using the JWT Claim feature (in the POC the name of the claim is access_level). Authorization is validated when a request is received and before any other action using the user input information. The access token is passed with every message sent to the message endpoint and a blacklist is used in order to allow the user to request an explicit token invalidation. Explicit token invalidation is interesting from a user point of view because, often when token are used, the validity timeframe of the token is relatively long (it's common to see a valid timeframe superior to 1 hour) so it's important to allow a user to have a way to indicate to the system "OK, i have finished my exchange with you so you can close our exchange session and cleanup associated links". It's help also a user to revoke itself is current access if a malicious concurrent access is detected using the same token (case of token stealing). Token blacklist - Maintain a temporary list using memory and time limited Caching of hashes of token that are not allowed to be used anymore import org.apache.commons.jcs.JCS;
import org.apache.commons.jcs.access.CacheAccess;
import org.apache.commons.jcs.access.exception.CacheException;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Utility class to manage the access token that have been declared as no more usable (explicit user logout)
*/
public class AccessTokenBlacklistUtils {
/**
* Message content send by user that indicate that the access token that come along the message must be blacklisted for further usage
*/
public static final String MESSAGE_ACCESS_TOKEN_INVALIDATION_FLAG = "INVALIDATE_TOKEN";
/**
* Use cache to store blacklisted token hash in order to avoid memory exhaustion and be consistent because token are valid 30 minutes so the item live in cache 60 minutes
*/
private static final CacheAccess<String, String> TOKEN_CACHE;
static {
try {
TOKEN_CACHE = JCS.getInstance("default");
} catch (CacheException e) {
throw new RuntimeException("Cannot init token cache !", e);
}
}
/**
* Add token into the blacklist
*
* @param token Token for which the hash must be added
* @throws NoSuchAlgorithmException If SHA256 is not available
*/
public static void addToken(String token) throws NoSuchAlgorithmException {
if (token != null && !token.trim().isEmpty()) {
String hashHex = computeHash(token);
if (TOKEN_CACHE.get(hashHex) == null) {
TOKEN_CACHE.putSafe(hashHex, hashHex);
}
}
}
/**
* Check if a token is present in the blacklist
*
* @param token Token for which the presence of the hash must be verified
* @return TRUE if token is blacklisted
* @throws NoSuchAlgorithmException If SHA256 is not available
*/
public static boolean isBlacklisted(String token) throws NoSuchAlgorithmException {
boolean exists = false;
if (token != null && !token.trim().isEmpty()) {
String hashHex = computeHash(token);
exists = (TOKEN_CACHE.get(hashHex) != null);
}
return exists;
}
/**
* Compute the SHA256 hash of a token
*
* @param token Token for which the hash must be computed
* @return The hash encoded in HEX
* @throws NoSuchAlgorithmException If SHA256 is not available
*/
private static String computeHash(String token) throws NoSuchAlgorithmException {
String hashHex = null;
if (token != null && !token.trim().isEmpty()) {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(token.getBytes());
hashHex = DatatypeConverter.printHexBinary(hash);
}
return hashHex;
}
}
Message handling - Process a request from a user to add a message in the list. Show a authorization validation approach example import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.owasp.pocwebsocket.enumeration.AccessLevel;
import org.owasp.pocwebsocket.util.AccessTokenBlacklistUtils;
import org.owasp.pocwebsocket.util.AuthenticationUtils;
import org.owasp.pocwebsocket.util.MessageUtils;
import org.owasp.pocwebsocket.vo.MessageRequest;
import org.owasp.pocwebsocket.vo.MessageResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.EncodeException;
import javax.websocket.RemoteEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Handle message flow
*/
public class MessageHandler implements javax.websocket.MessageHandler.Whole<MessageRequest> {
private static final Logger LOG = LoggerFactory.getLogger(MessageHandler.class);
/**
* Reference to the communication channel with the client
*/
private RemoteEndpoint.Basic clientConnection;
/**
* Constructor
*
* @param clientConnection Reference to the communication channel with the client
*/
public MessageHandler(RemoteEndpoint.Basic clientConnection) {
this.clientConnection = clientConnection;
}
/**
* {@inheritDoc}
*/
@Override
public void onMessage(MessageRequest message) {
MessageResponse response = null;
try {
/*Step 1: Verify the token*/
String token = message.getToken();
//Verify if is it in the blacklist
if (AccessTokenBlacklistUtils.isBlacklisted(token)) {
throw new IllegalAccessException("Token is in the blacklist !");
}
//Verify the signature of the token
DecodedJWT decodedToken = AuthenticationUtils.validateToken(token);
/*Step 2: Verify the authorization (access level)*/
Claim accessLevel = decodedToken.getClaim("access_level");
if (accessLevel == null || AccessLevel.valueOf(accessLevel.asString()) == null) {
throw new IllegalAccessException("Token have an invalid access level claim !");
}
/*Step 3: Do the expected processing*/
//Init the list of the messages for the current user
if (!MessageUtils.MESSAGES_DB.containsKey(decodedToken.getSubject())) {
MessageUtils.MESSAGES_DB.put(decodedToken.getSubject(), new ArrayList<>());
}
//Add message to the list of message of the user if the message is a not a token invalidation order otherwise add the token to the blacklist
if (AccessTokenBlacklistUtils.MESSAGE_ACCESS_TOKEN_INVALIDATION_FLAG.equalsIgnoreCase(message.getContent().trim())) {
AccessTokenBlacklistUtils.addToken(message.getToken());
} else {
MessageUtils.MESSAGES_DB.get(decodedToken.getSubject()).add(message.getContent());
}
//According to the access level of user either return only is message or return all message
List<String> messages = new ArrayList<>();
if (accessLevel.asString().equals(AccessLevel.USER.name())) {
MessageUtils.MESSAGES_DB.get(decodedToken.getSubject()).forEach(s -> messages.add(String.format("(%s): %s", decodedToken.getSubject(), s)));
} else if (accessLevel.asString().equals(AccessLevel.ADMIN.name())) {
MessageUtils.MESSAGES_DB.forEach((k, v) -> v.forEach(s -> messages.add(String.format("(%s): %s", k, s))));
}
//Build the response object indicating that exchange succeed
if (AccessTokenBlacklistUtils.MESSAGE_ACCESS_TOKEN_INVALIDATION_FLAG.equalsIgnoreCase(message.getContent().trim())) {
response = new MessageResponse(true, messages, "Token added to the blacklist");
}else{
response = new MessageResponse(true, messages, "");
}
} catch (Exception e) {
LOG.error("[MessageHandler] Error occur in exchange process.", e);
//Build the response object indicating that exchange fail
//We send the error detail on client because ware are in POC (it will not the case in a real app)
response = new MessageResponse(false, new ArrayList<>(), "Error occur during exchange: " + e.getMessage());
} finally {
//Send response
try {
this.clientConnection.sendObject(response);
} catch (IOException | EncodeException e) {
LOG.error("[MessageHandler] Error occur in response object sending.", e);
}
}
}
}
Confidentiality and IntegrityIf the raw version of the protocol is used (protocol WS://) then the transfered data are exposed to eavesdropping and potential on-the-fly alteration. Example of capture using Wireshark and searching for password exchanges in the stored PCAP file, not printable characters has been explicitly removed from the command result: $ grep -aE '(password)' capture.pcap
{"login":"bob","password":"bob123"}
There is a way to check, at WebSocket endpoint level, if the channel is secure by calling the method isSecure() on the session object instance. Example of implementation in the method of the endpoint in charge of setup the session and affect the message handler: /**
* Handle the beginning of an exchange
*
* @param session Exchange session information
*/
@OnOpen
public void start(Session session) {
...
//Affect a new message handler instance in order to process the exchange only if the channel is secured
if(session.isSecure()) {
session.addMessageHandler(new AuthenticationMessageHandler(session.getBasicRemote()));
}else{
LOG.info("[AuthenticationEndpoint] Session {} do not use a secure channel so no message handler was affected for processing and session was explicitly closed !", session.getId());
try{
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT,"Insecure channel used !"));
}catch(IOException e){
LOG.error("[AuthenticationEndpoint] Session {} cannot be explicitly closed !", session.getId(), e);
}
}
LOG.info("[AuthenticationEndpoint] Session {} message handler affected for processing", session.getId());
}
Expose WebSocket endpoints only on WSS:// protocol (WebSockets over SSL/TLS) in order to ensure Confidentiality and Integrity of the traffic like using HTTP over SSL/TLS to secure HTTP exchanges. Authors and Primary Editors
Other Cheatsheets |


|
fop 1.1 | |||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Object org.apache.fop.fo.flow.table.GridUnit
public class GridUnit
This class represents one grid unit inside a table.
| Field Summary | |
|---|---|
protected TableCell |
cell
Table cell which occupies this grid unit |
protected CollapsingBorderModel |
collapsingBorderModel
The border model helper associated with the table |
static int |
FIRST_IN_PART
Indicates that the grid unit is in the first row of the table part (header, footer, body). |
static int |
KEEP_WITH_NEXT_PENDING
Indicates that the primary grid unit has a pending keep-with-next. |
static int |
KEEP_WITH_PREVIOUS_PENDING
Indicates that the primary grid unit has a pending keep-with-previous. |
static int |
LAST_IN_PART
Indicates that the grid unit is in the last row of the table part (header, footer, body). |
| Constructor Summary | |
|---|---|
protected |
GridUnit(TableCell cell,
int colSpanIndex,
int rowSpanIndex)
Creates a new grid unit. |
protected |
GridUnit(Table table,
int colSpanIndex,
int rowSpanIndex)
Creates a new grid unit. |
| Method Summary | |
|---|---|
CommonBorderPaddingBackground.BorderInfo |
getBorderAfter(int which)
Returns the resolved border-after of this grid unit, in the collapsing-border model. |
CommonBorderPaddingBackground.BorderInfo |
getBorderBefore(int which)
Returns the resolved border-before of this grid unit, in the collapsing-border model. |
CommonBorderPaddingBackground.BorderInfo |
getBorderEnd()
Returns the resolved border-end of this grid unit, in the collapsing-border model. |
CommonBorderPaddingBackground.BorderInfo |
getBorderStart()
Returns the resolved border-start of this grid unit, in the collapsing-border model. |
TableCell |
getCell()
Returns the table cell associated with this grid unit. |
int |
getColSpanIndex()
|
boolean |
getFlag(int which)
Returns a flag for this GridUnit. |
PrimaryGridUnit |
getPrimary()
Returns the before-start grid unit of the cell containing this grid unit. |
TableRow |
getRow()
Returns the fo:table-row element (if any) this grid unit belongs to. |
int |
getRowSpanIndex()
|
boolean |
isEmpty()
Does this grid unit belong to an empty cell? |
boolean |
isLastGridUnitColSpan()
|
boolean |
isLastGridUnitRowSpan()
|
boolean |
isPrimary()
Is this grid unit the before-start grid unit of the cell? |
protected void |
setBordersFromCell()
Prepares the borders of this grid unit for upcoming resolution, in the collapsing model. |
void |
setFlag(int which)
Sets the given flag on this grid unit. |
void |
setFlag(int which,
boolean value)
Sets a flag on a GridUnit. |
java.lang.String |
toString()
|
| Methods inherited from class java.lang.Object |
|---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
| Field Detail |
|---|
public static final int FIRST_IN_PART
public static final int LAST_IN_PART
public static final int KEEP_WITH_NEXT_PENDING
public static final int KEEP_WITH_PREVIOUS_PENDING
protected TableCell cell
protected CollapsingBorderModel collapsingBorderModel
| Constructor Detail |
|---|
protected GridUnit(Table table, int colSpanIndex, int rowSpanIndex)
table
- the containing tablecolSpanIndex
- index of this grid unit in the span, in column directionrowSpanIndex
- index of this grid unit in the span, in row directionprotected GridUnit(TableCell cell, int colSpanIndex, int rowSpanIndex)
cell
- table cell which occupies this grid unitcolSpanIndex
- index of this grid unit in the span, in column directionrowSpanIndex
- index of this grid unit in the span, in row direction| Method Detail |
|---|
protected void setBordersFromCell()
public TableCell getCell()
public TableRow getRow()
public PrimaryGridUnit getPrimary()
public boolean isPrimary()
public boolean isEmpty()
public boolean isLastGridUnitColSpan()
public boolean isLastGridUnitRowSpan()
public int getRowSpanIndex()
public int getColSpanIndex()
public CommonBorderPaddingBackground.BorderInfo getBorderBefore(int which)
which
- one of ConditionalBorder.NORMAL
,
ConditionalBorder.LEADING_TRAILING
or ConditionalBorder.REST
public CommonBorderPaddingBackground.BorderInfo getBorderAfter(int which)
which
- one of ConditionalBorder.NORMAL
,
ConditionalBorder.LEADING_TRAILING
or ConditionalBorder.REST
public CommonBorderPaddingBackground.BorderInfo getBorderStart()
public CommonBorderPaddingBackground.BorderInfo getBorderEnd()
public boolean getFlag(int which)
which
- the requested flag
public void setFlag(int which, boolean value)
which
- the flag to setvalue
- the new value for the flagpublic void setFlag(int which)
which
- the flag to setpublic java.lang.String toString()
toString
in class java.lang.Object
|
fop 1.1 | |||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |


Preparation time
less than 30 mins
Cooking time
30 mins to 1 hour
Serves
Serves 6-8
Dietary
Preheat oven to 190C/380F/Gas 5.
Butter two 20cm/8in round baking tins then line with buttered baking parchment.
Beat the butter and sugar together until they are light and fluffy.
Sieve the flour, baking powder and cocoa together into a clean bowl.
Beat the eggs into the butter and sugar mixture, one at a time.
Gently fold in the sieved flour with a metal spoon.
Divide the mixture between the two prepared cake tins and place into the oven to bake for 20-25 minutes, or until the cake tops spring back when lightly pressed with your finger.
Remove from the oven and leave the cakes to cool in their tins for five minutes, then turn out onto a wire rack, remove the baking parchment and leave until they are completely cool.
Meanwhile, for the filling, drain the cherries, reserving six tablespoons of the syrup. Place the reserved syrup into a small pan and heat gently.
Mix the arrowroot with two tablespoons of water and add to the heated syrup, stirring constantly until the sauce has thickened.
Add one tablespoon of the rum, then remove from the heat and leave to cool.
Slice the sponge cakes in half and sprinkle each half with the remaining rum.
Cut the drained morello cherries in half, (removing their stones if necessary). Leave 10-12 cherries whole for decoration.
Whip the cream and the sugar together in a bowl until soft peaks form when the whisk is removed.
Reserve half of the cream mixture to cover the top of the cake. Stir the halved cherries and the heated syrup.to the remaining half of the cream.
Spread a third of the cream and cherry mixture onto one side of one layer of the cake. Add a second layer and spread with more of the cherry cream. Add a third layer of cake and spread the remaining cherry cream over the top.
Top with the final piece of cake and top with the reserved whipped cream.
Grate the dark chocolate over the top and decorate with the reserved whole cherries.


http://erinhyejin.github.io/
<EMAIL_ADDRESS>|<PHONE_NUMBER>
www.linkedin.com/in/erinhyejin
<EMAIL_ADDRESS>|<PHONE_NUMBER>
www.linkedin.com/in/erinhyejin
Erin Hyejin Kim
Skills
- Languages: Ruby, JavaScript, Python
- Frameworks: Ruby on Rails, Sinatra
- Web Technologies: jQuery, Ajax, CSS
- Database: ActiveRecord, MongoDB, PostgreSQL
Expertise
- Full-Stack Engineering
- Information Science
- Health/Medical Science
- Education
Projects
- Sexy K-pop: Web application that provides Korean pop music videos with collections, filters and search. [Big Brain Lab]
Ruby on Rails, Ajax, jQuery, ActiveRecord with PostgreSQL, Youtube API, bootstrap:
https://github.com/ErinHyejin/kpop - Celestial: A meteorite discovery portal using NASA's meteorite records and some APIs.
Ruby on Rails, Ajax, jQuery, ActiveRecord with PostgreSQL, Google api, WebGL api, bootstrap:
celestial-dbc-final.herokuapp.com
Education
| Dev Bootcamp | Immersive Educational Program for Coding | Dec 2014 - May 2015 |
| Ruby on Rails and related technologies | San Francisco, CA | |
| The University of Texas at Austin | School of Information | 2011 |
| Master's Program (Non-degree Seeker) | Austin, TX | |
| Yonsei University | Education, English Literature and Linguistics | Mar 2004 - Jun 2009 |
| Bachelor's Degree | Seoul, Korea |
Experience
| Big Brain Lab | Backend web deleoper | May 2015 - Current |
| Working on database, youtube API, rating system, sorting/filtering, searching videos, collection, and other functions using Ruby on rails | San Mateo, CA | |
| St.David's Medical Center | Volunteer at Medical-Surgical Nursing Unit | May 2012 - Nov 2012 |
| Charted, assisted nurses, discharged patients | Austin, TX | |
| The QS-APPLE (Asia-Pacific Professional Leaders in Education) | Translator at the Annual Conference | July 2008 |
| VIP guest services, managed meeting rooms | Seoul, Korea |


OOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The code monkeys at our headquarters are working VEWY HAWD to fix this!
← Go BackOOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The code monkeys at our headquarters are working VEWY HAWD to fix this!
← Go Back


I build affordable websites for freelancers and small businesses.
GitHub repositories for some of my best projects
My latest posts on web development, technology and learning


Overview
Package
Class
Use
Tree
Deprecated
Index
Help
PREV LETTER
NEXT LETTER
FRAMES
NO FRAMES
All Classes
A
B
C
D
E
F
G
H
I
J
L
M
N
O
P
R
S
T
U
V
W
L
launch(Vec2, Vec2, boolean)
- Method in class fr.umlv.escapeirandroid.entity.
Fireball
This method takes an origin, the ship launching, a destination the targeted ship, and a boolean to determine if the weapon is launched by the heroship
launch(Vec2, Vec2, boolean)
- Method in interface fr.umlv.escapeirandroid.entity.
Launchable
Launch.
launch(Vec2, Vec2, boolean)
- Method in class fr.umlv.escapeirandroid.entity.
Missile
This method takes an origin, the ship launching, a destination the targeted ship, and a boolean to determine if the weapon is launched by the heroship
launch(Vec2, Vec2, boolean)
- Method in class fr.umlv.escapeirandroid.entity.
Shiboleet
This method takes an origin, the ship launching, a destination the targeted ship, and a boolean to determine if the weapon is launched by the heroship
launch(Vec2, Vec2, boolean)
- Method in class fr.umlv.escapeirandroid.entity.
Triforce
This method takes an origin, the ship launching, a destination the targeted ship, and a boolean to determine if the weapon is launched by the heroship
Launchable
- Interface in
fr.umlv.escapeirandroid.entity
The Interface Launchable defines the comportement of something launchable.
LEVEL
- Static variable in class fr.umlv.escapeirandroid.game.
EscapeIR
Level
- Class in
fr.umlv.escapeirandroid.game
The Class Level.
Level(GameBoard)
- Constructor for class fr.umlv.escapeirandroid.game.
Level
Instantiates a new level.
LevelPropertiesFragment
- Class in
fr.umlv.escapeirandroid.editor
The Class LevelPropertiesFragment provides a fragment with a form to edit the level properties, e.g the name, the duration and the background
LevelPropertiesFragment()
- Constructor for class fr.umlv.escapeirandroid.editor.
LevelPropertiesFragment
LevelPropertiesFragment.LevelPropertiesListener
- Interface in
fr.umlv.escapeirandroid.editor
load()
- Method in class fr.umlv.escapeirandroid.entity.
AbstractWeapon
Loads the weapon
loadAvailableLevel()
- Method in class fr.umlv.escapeirandroid.utils.
FileManager
List the available level from the app local storage
loadBackgroundImage(String)
- Method in class fr.umlv.escapeirandroid.utils.
ImageManager
Loads a scaled background
loadGameBoard(String)
- Method in class fr.umlv.escapeirandroid.utils.
FileManager
Loads a gameboard XML file from it's name passed as a String.
loadGestureLibrary()
- Method in class fr.umlv.escapeirandroid.utils.
FileManager
Loads the gesture library from asset
loadHeroWeapon()
- Static method in class fr.umlv.escapeirandroid.game.
Level
Load hero weapon.
loadImage(int)
- Method in class fr.umlv.escapeirandroid.utils.
ImageManager
Loads an image from it's name and extension passed as a String.
loadImageFromInternalCacheStorage(String)
- Method in class fr.umlv.escapeirandroid.utils.
ImageManager
Loads an image from the internalCache
loadImageFromPath(String)
- Method in class fr.umlv.escapeirandroid.utils.
ImageManager
Loads an image from a path
loadImageLevelEditor(String)
- Method in class fr.umlv.escapeirandroid.utils.
ImageManager
Scales Image for the level editor
loadLevelConfigFile(InputStream)
- Method in class fr.umlv.escapeirandroid.utils.
FileManager
This method parses a gameboard XML file as it should be formed and returns the object GameBoad according to the values in the file
loadLevelOverHttp(URL)
- Method in class fr.umlv.escapeirandroid.utils.
FileManager
Loads a level overt Internet
loadWeapon()
- Method in class fr.umlv.escapeirandroid.entity.
HeroShip
Begin the loading sequence of the selected weapon.
Overview
Package
Class
Use
Tree
Deprecated
Index
Help
PREV LETTER
NEXT LETTER
FRAMES
NO FRAMES
All Classes
A
B
C
D
E
F
G
H
I
J
L
M
N
O
P
R
S
T
U
V
W


Model
.Files
for OPC UA binary encoding and
therefore provides mechanims to encode and decode a file.MediaType
with prefdefined media types
that are neccessary for LD-Communication.Properties
that are handy for using this framework.Copyright © 2018. All rights reserved.


c6-control
When programming, what are some of your triggers? What causes a disproportionate emotional response?
To be honest, there are not any triggers so far for me when programming.
Maybe because I set a low bar for myself during phase 0 and know that being stuck is normal.
I know how to be friend with being stuck.
How did you find the Siberian North Railroad exercise (Stop, Breathe, Notice, Reflect, Respond)?
I did have some triggers in personal life and I used this exercise before realizing
that it was named Siberian North Railroad exercise this week. I must have learned it from some parenting books
(I read a lot of parenting books to be a reasonable good mum).It was helpful.


I'm a graphic artist, photoshop using, photographer hobbyist with a career in warehouse management, turned coding adventurer with endless opportunities in the developer field. Below is a starting point to a web app I’ve been working on as a final project for my graduating piece at the Iron Yard. Browse through the Time Tracker Pro and you will see the solution to a problem I’ve encountered in the management field dealing with tracking multiple shift schedules. The answer is my web app that allows one to track the daily schedule needs of your team and generate a report to display the range of data collected over time. For more information about my app, feel free to reach out and make a connection or just say hello.
Graduate of the .NET Developer Course at The Iron Yard. This was a 12 week fully Immersive course that constantly challenged me.
I focused on the logic and database needs required to run and scale web applications. I’ve combined databases, APIs, and user interface frameworks. I’ve deployed fully formed products to the cloud, integrate with online services, and enable mobile and front end applications.
I started out with a simple console log window “Hello World” on the first day. I advanced into Web API’s, ASP.NET MVC , JQuery , Ajax, Bootstrap and AngularJS.
I wrote code in C# and covered a range of other languages used in my studies and free time. This allowed me to combine JSON, SQL, HTML, CSS and JavaScript in Visual Studios to work on the project featured.


SAMPLE *load_sample(char *filename);
Loads a sample from a file, returning a pointer to it, or NULL on error.
At present this function supports both mono and stereo WAV and mono VOC
files, in 8 or 16 bit formats.
SAMPLE *load_wav(char *filename);
Loads a sample from a RIFF WAV file.
SAMPLE *load_voc(char *filename);
Loads a sample from a Creative Labs VOC file.
SAMPLE *create_sample(int bits, int stereo, int freq, int len);
Constructs a new sample structure of the specified type. The data field
points to a block of waveform data: see the structure definition in
allegro.h for details.
void destroy_sample(SAMPLE *spl);
Destroys a sample structure when you are done with it. It is safe to call
this even when the sample might be playing, because it checks and will
kill it off if it is active.
int play_sample(SAMPLE *spl, int vol, int pan, int freq, int loop);
Triggers a sample at the specified volume, pan position, and frequency.
The volume and pan range from 0 (min/left) to 255 (max/right). Frequency
is relative rather than absolute: 1000 represents the frequency that the
sample was recorded at, 2000 is twice this, etc. If the loop flag is set,
the sample will repeat until you call stop_sample(), and can be
manipulated while it is playing by calling adjust_sample().
void adjust_sample(SAMPLE *spl, int vol, int pan, int freq, int loop);
Alters the parameters of a sample while it is playing (useful for
manipulating looped sounds). You can alter the volume, pan, and
frequency, and can also clear the loop flag, which will stop the sample
when it next reaches the end of its loop. If there are several copies of
the same sample playing, this will adjust the first one it comes across.
If the sample is not playing it has no effect.
void stop_sample(SAMPLE *spl);
Kills off a sample, which is required if you have set a sample going in
looped mode. If there are several copies of the sample playing, it will
stop them all.
If you need more detailed control over how samples are played, you can use the lower level voice functions rather than just calling play_sample(). This is rather more work, because you have to explicitly allocate and free the voices rather than them being automatically released when they finish playing, but allows far more precise specification of exactly how you want everything to sound. You may also want to modify a couple of fields from the sample structure:
int priority; Ranging 0-255 (default 128), this controls how voices are allocated if you attempt to play more than the driver can handle. This may be used to ensure that the less important sounds are cut off while the important ones are preserved.int allocate_voice(SAMPLE *spl);unsigned long loop_start; unsigned long loop_end; Loop position in sample units, by default set to the start and end of the sample.
void deallocate_voice(int voice);
Frees a soundcard voice, stopping it from playing and releasing whatever
resources it is using.
void reallocate_voice(int voice, SAMPLE *spl);
Switches an already-allocated voice to use a different sample. Calling
reallocate_voice(voice, sample) is equivalent to:
deallocate_voice(voice); voice = allocate_voice(sample);void release_voice(int voice);
void voice_start(int voice);
Activates a voice, using whatever parameters have been set for it.
void voice_stop(int voice);
Stops a voice, storing the current position and state so that it may
later be resumed by calling voice_start().
void voice_set_priority(int voice, int priority);
Sets the priority of a voice (range 0-255). This is used to decide which
voices should be chopped off, if you attempt to play more than the
soundcard driver can handle.
SAMPLE *voice_check(int voice);
Checks whether a voice is currently allocated. It returns a copy of the
sample that the voice is using, or NULL if the voice is inactive (ie. it
has been deallocated, or the release_voice() function has been called and
the sample has then finished playing).
int voice_get_position(int voice);
Returns the current position of a voice, in sample units, or -1 if it has
finished playing.
void voice_set_position(int voice, int position);
Sets the position of a voice, in sample units.
void voice_set_playmode(int voice, int playmode);
Adjusts the loop status of the specified voice. This can be done while
the voice is playing, so you can start a sample in looped mode (having
set the loop start and end positions to the appropriate values), and then
clear the loop flag when you want to end the sound, which will cause it
to continue past the loop end, play the subsequent part of the sample,
and finish in the normal way. The mode parameter is a bitfield containing
the following values:
void voice_set_volume(int voice, int volume);
Sets the volume of the voice, range 0-255.
void voice_ramp_volume(int voice, int time, int endvol);
Starts a volume ramp (crescendo or diminuendo) from the current volume to
the specified ending volume, lasting for time milliseconds.
void voice_stop_volumeramp(int voice);
Interrupts a volume ramp operation.
int voice_get_frequency(int voice);
Returns the current pitch of the voice, in Hz.
void voice_set_frequency(int voice, int frequency);
Sets the pitch of the voice, in Hz.
void voice_sweep_frequency(int voice, int time, int endfreq);
Starts a frequency sweep (glissando) from the current pitch to the
specified ending pitch, lasting for time milliseconds.
void voice_stop_frequency_sweep(int voice);
Interrupts a frequency sweep operation.
int voice_get_pan(int voice);
Returns the current pan position, from 0 (left) to 255 (right).
void voice_set_pan(int voice, int pan);
Sets the pan position, ranging from 0 (left) to 255 (right).
void voice_sweep_pan(int voice, int time, int endpan);
Starts a pan sweep (left <-> right movement) from the current position to
the specified ending position, lasting for time milliseconds.
void voice_stop_pan_sweep(int voice);
Interrupts a pan sweep operation.
void voice_set_echo(int voice, int strength, int delay);
Sets the echo parameters for a voice (not currently implemented).
void voice_set_tremolo(int voice, int rate, int depth);
Sets the tremolo parameters for a voice (not currently implemented).
void voice_set_vibrato(int voice, int rate, int depth);
Sets the vibrato parameters for a voice (not currently implemented).