WebSphere Portal Security Considerations

Setting headers on IBM HTTP Server

Stop the HTTP server.
Log on to the web server and edit the following file:
/opt/IBM/HTTPServer/conf/httpd.conf
Enable below module –

LoadModule headers_module modules/mod_headers.so

<ifModule mod_headers.c>
Header set X-Permitted-Cross-Domain-Policies “none”
Header set X-XSS-Protection “1; mode=block”
Header set X-Frame-Options “DENY”
</ifModule>

Restart the HTTP server.

Setting headers on WebSphere Application server – optional

WebSphere Portal offers protection against XSS, enabled with the security.css.protection setting in the Configuration Service.

Above setting should be enabled by default

WebSphere Application Server offers protection against XSS, enabled with the com.ibm.ws.security.addHttpOnlyAttributeToCookies custom property for Global Security. When set, the HTTP-only attribute, HttpOnly, will be set for LTPA tokens. Browsers will not allow scripts access to cookies for which HTTPOnly is set

X-Frame-Options Response Header

Log into WebSphere Integrated Solutions Console as the WebSphere Administrator user.
In the left panel, open Servers, and then open Server Types. Click WebSphere application servers.
In the Application servers table, click the server where InfoSphere Business Glossary is installed.
Under Server Infrastructure, open Java and Process Management, and then click Process definition.
Under Additional Properties, click Java Virtual Machine.
Under Additional Properties, click Custom Properties.
Click New. Complete these steps to create and apply a system configuration property:
In the Name field, type bg.xFrameOptions.
In the Value field, type in the X-Frame-Option HTTP response header that you need (for example: SAMEORIGIN).
Click Apply, and then click OK.
In the Messages window, click Save to save the new property when WebSphere Application Server is restarted.
Stop and restart WebSphere Application Server.

JBoss Fuse ESB Local Env Setup

Useful Instructions for setting up JBoss Fuse ESB on EAP:

https://developers.redhat.com/products/fuse/get-started/#tab-eapRuntime

Please install Java8 and Apache Maven.

==========================Java 8======================

Download JDK 8 – 1.8.0_20 : http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

=================Apache Maven : Download link=================================

https://maven.apache.org/download.cgi?Preferred=ftp://mirror.reverse.net/pub/apache/

Windows Instructions:
Check environment variable value e.g.

C:\Windows\system32>echo %JAVA_HOME%
C:\Program Files\Java\jdk1.8.0_20
Adding to PATH: Add the unpacked distribution’s bin directory to your user PATH environment variable by opening up the system properties (WinKey + Pause), selecting the “Advanced” tab, and the “Environment Variables” button, then adding or selecting the PATH variable in the user variables with the value C:\Program Files\apache-maven-3.3.9\bin.

The same dialog can be used to set JAVA_HOME to the location of your JDK, e.g. C:\Program Files\Java\jdk1.8.0_20

Open a new command prompt (Winkey + R then type cmd) and run mvn -v to verify the installation.
==============End of Apache Maven=============================================

Also signup for redhat developer website as it might be required later.

https://developers.redhat.com/auth/realms/rhd/protocol/openid-connect/registrations?client_id=web&redirect_uri=https%3A%2F%2Fdevelopers.redhat.com%2F%2Fconfirmation&state=cd08b2de-4ca5-4802-a670-c6bb22da3c83&nonce=566e644b-0598-4192-8b2e-fdc90c25475e&response_mode=fragment&response_type=code

Sometimes connecting to maven is tricky , below sample settings file can be used as a reference.


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<profiles>
<profile>
<id>jboss-ga</id>
<repositories>
<repository>
<id>jboss-ga-repository</id>
<name>JBoss GA Tech Preview Maven Repository</name>
<url>http://maven.repository.redhat.com/techpreview/all</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>jboss-ga-plugin-repository</id>
<name>JBoss 6 Maven Plugin Repository</name>
<url>http://maven.repository.redhat.com/techpreview/all</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>

</profile>
<profile>
<id>jboss-earlyaccess-repository</id>
<repositories>
<repository>
<id>jboss-earlyaccess-repository</id>
<name>jboss-earlyaccess-repository</name>
<url>http://maven.repository.redhat.com/earlyaccess/all/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>jboss-earlyaccess-repository</id>
<name>jboss-earlyaccess-repository</name>
<url>http://maven.repository.redhat.com/earlyaccess/all/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>jboss-ga</activeProfile>
</activeProfiles>
<proxies>
<proxy>
<active>true</active>
<protocol>http</protocol>
<host>proxyhostname</host>
<port>8082</port>
<username/>
<password/>
<nonProxyHosts>localhost,127.0.0.1</nonProxyHosts>
</proxy>
</proxies>

</settings>

 

Primefaces input number retun 0 for empty value

Ever experienced p:inputNumber tag with empty value submitted and bean storing them as 0’s instead of null or empty ? Its because of org.apache.el.parser.COERCE_TO_ZERO , see below how we can resolve this issue.

I’m using InputNumber with emptyValue=”empty”. But empty input fields always display zero instead of blank or empty value

<p:inputNumber size=”5″ emptyValue=”empty” id=”MSSONB” value=”#{pageBean.mar.MSSONB}” widgetVar=”MSSONBVar” />

Primefaces : 6.0.8
JSF 2.0
WebSphere Portal 8

Example in primefaces showcase seems to work – http://www.primefaces.org/showcase/ui/i … mber.xhtml
but when we try to test the same code as in showcase it does not work.

org.apache.el.parser.COERCE_TO_ZERO

Allows for the expression language (EL) that WebSphere Application Server uses to coerce null and empty string integer values to a 0 value or for NOT allowing a coerce to a 0 value and retaining the null or empty string integer. The default is true and permits a null or empty string integer value to be coerced to a 0 value.
Important: To keep a null value from being coerced to a 0 value in a MyFaces application, the following context parameter in the web.xml of the application: should be set to ensure that all possible instances of an empty or null value are inhibited from being coerced to zero.
<context-param>
<param-name>javax.faces.
INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
</param-name>
<param-value>true</param-value>
</context-param>
You set the org.apache.el.parser.COERCE_TO_ZERO property using the administrative console.
Expand Servers, and then select WebSphere Application Servers. Click on the appropriate server from the list.
Under Server Infrastructure, expand Java and Process Management > Click on Process definition.
Under Additional Properties, click Java virtual machine.
Under Additional Properties, click Custom properties.
Click New and add the org.apache.el.parser.COERCE_TO_ZERO property with the value of false if you do NOT want a null value coerced to zero.
Click Save to save the change and restart the WebSphere Application Server to ensure that the change takes place.

Decode SAML Authentication Request

Decode Authenticate Request :

samlsample


/*
* Retrieves the AuthnRequest from the encoded and compressed String extracted
* from the URL. The AuthnRequest XML is retrieved in the following order: <p>
* 1. URL decode <br> 2. Base64 decode <br> 3. Inflate <br> Returns the String
* format of the AuthnRequest XML.
*/
public static String decodeAuthnRequestXML(String encodedRequestXmlString)
throws SamlException {
try {
// URL decode
// No need to URL decode: auto decoded by request.getParameter() method

// Base64 decode
Base64 base64Decoder = new Base64();
byte[] xmlBytes = encodedRequestXmlString.getBytes("UTF-8");
byte[] base64DecodedByteArray = base64Decoder.decode(xmlBytes);

//Uncompress the AuthnRequest data
//First attempt to unzip the byte array according to DEFLATE (rfc 1951)
try {

Inflater inflater = new Inflater(true);
inflater.setInput(base64DecodedByteArray);
// since we are decompressing, it's impossible to know how much space we
// might need; hopefully this number is suitably big
byte[] xmlMessageBytes = new byte[5000];
int resultLength = inflater.inflate(xmlMessageBytes);

if (inflater.getRemaining()>0) {

System.out.println("Inflater Error :: didn't allocate enough space to hold decompressed data :: "+inflater.getRemaining());
throw new RuntimeException("didn't allocate enough space to hold "
+ "decompressed data");
}

inflater.end();
return new String(xmlMessageBytes, 0, resultLength, "UTF-8");

} catch (DataFormatException e) {

// if DEFLATE fails, then attempt to unzip the byte array according to
// zlib (rfc 1950)
ByteArrayInputStream bais = new ByteArrayInputStream(
base64DecodedByteArray);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InflaterInputStream iis = new InflaterInputStream(bais);
byte[] buf = new byte[1024];
int count = iis.read(buf);
while (count != -1) {
baos.write(buf, 0, count);
count = iis.read(buf);
}
iis.close();
return new String(baos.toByteArray());
}

} catch (UnsupportedEncodingException e) {
throw new SamlException("Error decoding AuthnRequest: " +
"Check decoding scheme - " + e.getMessage());
} catch (IOException e) {
throw new SamlException("Error decoding AuthnRequest: " +
"Check decoding scheme - " + e.getMessage());
}
}

 

WebSphere Portal oAuth2 – GAE

I have come up with below architecture to read GAE appspot data with oAuth2.0
1.Create Service account and private key using Google API console
2.Create JWT (JSON Web Token) on the portal application which wants to access the data from GAE
3.Pass JWT Token as part of the headers to appspot
4.Validate JWT token from appspot and send the request information in JSON format.
gae-oauth2

Sample code to create JWT :

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ResourceBundle;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.gson.JsonParser;

/**
* Servlet implementation class GoogleServiceAccount
*/
public class GoogleServiceAccountOAuth {

static String keyAlias = "privatekey";
public ResourceBundle bundle = ResourceBundle
.getBundle("com.ibm.ereportscursors.nl.EReportsCursorsPortletResource");
public static byte[] signData(byte[] data, PrivateKey privateKey)
throws InvalidKeyException, SignatureException,
NoSuchAlgorithmException {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}

public static String encodeBase64(byte[] rawData) {
byte[] data = Base64.encodeBase64(rawData);
return data.toString();
}

private static PrivateKey getPrivateKey(InputStream fis, String password)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException {
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(fis, password.toCharArray());
PrivateKey privateKey = (PrivateKey) keystore.getKey(keyAlias, password
.toCharArray());
return privateKey;
}

public String getAccessToken(String oAuth_EmailID) {
String token_str="";
String password = bundle.getString("Certificate_Pwd");
try {
String jwtHeaderStr = null;
String jwtClaimStr = null;
PrivateKey privateKey = null;

// JWT HEADER
JSONObject jwtHeader = new JSONObject();
try {
jwtHeader.put("alg", "RS256");
jwtHeader.put("typ", "JWT");
jwtHeaderStr = jwtHeader.toString();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

byte[] encodedHeader = Base64.encodeBase64(jwtHeaderStr
.getBytes("UTF-8"));

// JWT CLAIMSET
JSONObject jwtClaimSet = new JSONObject();
long iat = (System.currentTimeMillis() / 1000) - 60;
long exp = iat + 3600;
try {
jwtClaimSet
.put("iss",
oAuth_EmailID);
jwtClaimSet.put("scope",
bundle.getString("OAuth_Scope"));

jwtClaimSet.put("aud",
bundle.getString("OAuth_Aud"));
jwtClaimSet.put("exp", +exp);
jwtClaimSet.put("iat", +iat);
jwtClaimStr = jwtClaimSet.toString();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

byte[] encodedClaimSet = Base64.encodeBase64(jwtClaimStr
.getBytes("UTF-8"));

StringBuffer token = new StringBuffer();
token.append(new String(encodedHeader));
token.append(".");
token.append(new String(encodedClaimSet));

// JWT SIGNATURE
InputStream fis = this.getClass().getResourceAsStream(
"ereports_test1.p12");
privateKey = getPrivateKey(fis, password);
byte[] sig = signData(token.toString().getBytes("UTF-8"),
privateKey);
byte[] encodedSig = Base64.encodeBase64(sig);
System.out.println("Signature before encoding:"
+ new String(encodedSig));
String signedPayload = encodeBase64(sig);
// System.out.println("Signature before encoding:"+signedPayload);
token.append(".");
// token.append(signedPayload);
token.append(new String(encodedSig));

HttpClient client = new HttpClient();
PostMethod method = new PostMethod(
bundle.getString("OAuth_PostURL"));
method.addRequestHeader("Content-Type",
bundle.getString("OAuth_PostContentType"));
method.addParameter("grant_type",
bundle.getString("OAuth_Grant_Type"));
System.out.println("printing Token.toString():" + token.toString());
method.addParameter("assertion", token.toString());

try {
int responseCode = client.executeMethod(method);
System.out.println(responseCode);
String respop = method.getResponseBodyAsString();
System.out.println(method.getResponseBodyAsString());

JsonParser parser = new JsonParser();

token_str = parser.parse(respop).getAsJsonObject().get(
"access_token").getAsString();
} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return  token_str;

}

</div>
<div class="separator">

}

Oracle Datagaurd

We had to recently implement Oracle Datagaurd for websphere portal.

What is Oracle Datagaurd ?

Oracle Data Guard ensures high availability, data protection, and disaster recovery for enterprise data. Data Guard provides a comprehensive set of services that create, maintain, manage, and monitor one or more standby databases to enable production Oracle databases to survive disasters and data corruptions. Data Guard maintains these standby databases as transactionally consistent copies of the production database. Then, if the production database becomes unavailable because of a planned or an unplanned outage, Data Guard can switch any standby database to the production role, minimizing the downtime associated with the outage. Data Guard can be used with traditional backup, restoration, and cluster techniques to provide a high level of data protection and data availability.

Usually datagaurd URL which should be working is below in most of enterprise applications is below

jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS_LIST =(ADDRESS=(PROTOCOL=tcp)(HOST=SERVER1)(PORT=1524))(ADDRESS=(PROTOCOL=tcp)(HOST=SERVER2)(PORT=1524)))(LOAD_BALANCE = no)(FAILOVER = yes)(CONNECT_DATA =(SERVICE_NAME = wpsprd)(SERVER = DEDICATED)(FAILOVER_MODE =(TYPE = select)(METHOD = BASIC)(RETRIES = 18000)(DELAY = 2))))

For some reason this does not work with websphere portal, as  a workaround we updated websphere portal server host file with an entry like below.

datagaurd  Database server1 IP
#datagaurd  Database server2 IP

Updated all JDBC URL’s replacing server name with datagaurd.

After updating the jdbc url, we need to run the following commands:

./ConfigEngine.sh validate-database-driver -DWasPassword=XXXXX

./ConfigEngine.sh validate-database-connection -DWasPassword=XXXXX

./ConfigEngine.sh connect-database -DWasPassword=XXXXX