XSS detection in SAST tools - rest

SAST tool detects a XSS (reflected) vulnerability , How to check this is false positive or not?
#Path(RESET_CREDENTIALS_PATH)
#POST
public Response resetCredentialsPOST(#QueryParam(AUTH_SESSION_ID) String authSessionId, // optional, can get from cookie instead
#QueryParam(SESSION_CODE) String code,
#QueryParam(Constants.EXECUTION) String execution,
#QueryParam(Constants.CLIENT_ID) String clientId,
#QueryParam(Constants.TAB_ID) String tabId,
#QueryParam(Constants.KEY) String key) {
if (key != null) {
return handleActionToken(key, execution, clientId, tabId);
}
The Application 's resetCredentialsPost embeds untrusted data in the generated output with handleActionToken without proper sanitization.

Related

Sign PDF with new Belgian id card (eid middleware and itext)

I try to sign some pdf's with a Belgian id card.
To acheive that, I'm using the belgium eid middleware to sign the data and itext7 to stamp the pdf with the signature.
I use a PdfSigner (itext) and I have implement a IExternalSignature to call the eid middleware to sign the message.
All work well for the Belgian id card 1.7 with encryption RSA and hash SHA256.
But when I try to sign with the new Belgian id card 1.8 with encryption ECDSA and hash SHA384, the signature can't be validated by adobe reader (or other reader).
"The document has been altered or corrupted".
It seems to be a mismatch somewhere in the hashing or ...
I search for some days but I have no more idea to fix that ...
Someone have an idea about what is going wrong?
Thanks in advance for your help.
Here some additional informations.
The external signature class:
internal sealed class BeIDSignature : IExternalSignature
{
public string GetEncryptionAlgorithm()
{
return eidWrapper.Instance.GetEncryptionAlgorithm().ToString();
}
public string GetHashAlgorithm()
{
switch (EidWrapper.Instance.GetEncryptionAlgorithm())
{
case EidWrapper.EncryptionAmgorithm.RSA:
return DigestAlgorithms.SHA256;
case EidWrapper.EncryptionAmgorithm.ECDSA:
return DigestAlgorithms.SHA384;
default:
return null;
}
}
public byte[] Sign(byte[] message)
{
return EidWrapper.Instance.SignData(message);
}
}
GetEncryptionAlgorithm will return RSA or ECDSA depending of the chip.
The sign method will use the eid-mw packege to generate the signature.
A little piece of code of the sign method of the EidWrapper:
if (key.KeyType.KeyType == CKK.EC)
{
session.SignInit(new Mechanism(CKM.ECDSA_SHA384), key);
return session.Sign(data);
}
else if (key.KeyType.KeyType == CKK.RSA)
{
session.SignInit(new Mechanism(CKM.SHA256_RSA_PKCS), key);
return session.Sign(data);
}
You can find here a zip with 3 pdf files:
The original file
One signed directly with adobe (siganture is ok)
One signed with eid-mw and itext (signature is NOT ok). But remember
that is working for RSA/SHA256 siganture.
https://easyupload.io/yzscsu
Thanks again for your time.
Here is a sample of an external container for the belgian eid smartcard.
The code is not fully implemented but you have a base to make a siganture in ECDSA/SHA384 correctly.
Hope that will help someone :)
internal sealed class BeIdExternalSignatureContainer : IExternalSignatureContainer
{
private IX509Store _crls = null;
private readonly IHttpClientFactory _httpClientFactory;
private int _crlsSize = 0;
public BeIdExternalSignatureContainer(IHttpClientFactory httpClientFactory)
{
this._httpClientFactory = httpClientFactory;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.ETSI_CAdES_DETACHED);
}
public int ComputeEstimateSize()
{
// Base on itext estimation
if (this._crlsSize == 0)
{
this.InitializeCrls();
}
return (8192 + this._crlsSize + 4192) * 2 + 2;
}
public byte[] Sign(Stream data)
{
IReadOnlyDictionary<string, byte[]> certificatesBinaries = EidWrapper.Instance.GetCertificateFiles(new string[] {
Constants.SIGNATURE_CERTIFICATE_NAME,
Constants.CA_CERTIFICATE_NAME,
Constants.ROOT_CERTIFICATE_NAME
});
X509Certificate signCertificate = new(X509CertificateStructure.GetInstance(certificatesBinaries[Constants.SIGNATURE_CERTIFICATE_NAME]));
EidWrapper.EncryptionAlgorithms encryptionAlgorithm = EidWrapper.Instance.GetEncryptionAlgorithm();
SignerInfoGenerator signerGenerator = new SignerInfoGeneratorBuilder()
.WithSignedAttributeGenerator(new PadesSignedAttributeGenerator { SigningCertificate = signCertificate })
.WithUnsignedAttributeGenerator(new PadesUnsignedAttributeGenerator(this._httpClientFactory))
.Build(new BeIdSignatureFactory(encryptionAlgorithm), signCertificate);
CmsSignedDataGenerator gen = new();
gen.AddSignerInfoGenerator(signerGenerator);
IX509Store x509CertStore = X509StoreFactory.Create(
"Certificate/Collection",
new X509CollectionStoreParameters(certificatesBinaries.Values.Select(b => new X509Certificate(X509CertificateStructure.GetInstance(b))).ToList()));
gen.AddCertificates(x509CertStore);
gen.AddCrls(this.Crls);
CmsProcessableInputStream cmsProcessableInputStream = new(data);
CmsSignedData signedData = gen.Generate(cmsProcessableInputStream, false);
return signedData.GetEncoded();
}
private IX509Store Crls
{
get
{
if (this._crls == null)
{
this.InitializeCrls();
}
return this._crls;
}
}
private void InitializeCrls()
{
this._crlsSize = 0;
List<X509Crl> crls = new();
X509CrlParser crlParser = new();
HttpClient httpClient = this._httpClientFactory.CreateClient();
foreach (var crlUrl in BeIdSignatureConstants.CRL_URL_COLLECTION)
{
using HttpResponseMessage response = httpClient.Send(new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(crlUrl) });
if (response.IsSuccessStatusCode)
{
using MemoryStream ms = new MemoryStream();
response.Content.ReadAsStream().CopyTo(ms);
byte[] crlBytes = ms.ToArray();
this._crlsSize += crlBytes.Length;
crls.Add(crlParser.ReadCrl(crlBytes));
}
}
this._crls = X509StoreFactory.Create(
"CRL/Collection",
new X509CollectionStoreParameters(crls));
}
}
internal class BeIdSignatureFactory : ISignatureFactory
{
private readonly EidWrapper.EncryptionAlgorithms _encryptionAlgorithm;
public BeIdSignatureFactory(EidWrapper.EncryptionAlgorithms encryptionAlgorithm)
{
this._encryptionAlgorithm = encryptionAlgorithm;
}
public IStreamCalculator CreateCalculator()
{
BeIdSigner signer = new(this._encryptionAlgorithm);
signer.Init(true, null);
return new DefaultSignatureCalculator(signer);
}
public object AlgorithmDetails
{
get
{
return MapAlgorithm(this._encryptionAlgorithm);
}
}
public static AlgorithmIdentifier MapAlgorithm(EidWrapper.EncryptionAlgorithms encryptionAlgorithm)
{
switch (encryptionAlgorithm)
{
case EidWrapper.EncryptionAlgorithms.RSA:
return new AlgorithmIdentifier(PkcsObjectIdentifiers.Sha256WithRsaEncryption, DerNull.Instance);
case EidWrapper.EncryptionAlgorithms.ECDSA:
return new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha384, DerNull.Instance);
default:
throw new ArgumentException($"Unsupported encryption algorithm: {encryptionAlgorithm}");
}
}
}
internal class BeIdSigner : ISigner
{
private byte[] _input;
private readonly EidWrapper.EncryptionAlgorithms _encryptionAlgorithm;
public BeIdSigner(EidWrapper.EncryptionAlgorithms encryptionAlgorithm)
{
this._encryptionAlgorithm = encryptionAlgorithm;
}
public void Init(bool forSigning, ICipherParameters parameters)
{
this.Reset();
}
public void Update(byte input)
{
throw new NotImplementedException("The \"ISigner.Update\" method is not implemented for the \"BeIdSigner\" class.");
}
public void BlockUpdate(byte[] input, int inOff, int length)
{
this._input = input.Skip(inOff).Take(length).ToArray();
}
public byte[] GenerateSignature()
{
return this._encryptionAlgorithm == EidWrapper.EncryptionAlgorithms.ECDSA
? PlainToDer(EidWrapper.Instance.SignData(this._input))
: EidWrapper.Instance.SignData(this._input);
}
public bool VerifySignature(byte[] signature)
{
throw new NotImplementedException("The \"ISigner.VerifySignature\" method is not implemented for the \"BeIdSigner\" class.");
}
public void Reset()
{
this._input = null;
}
public string AlgorithmName => throw new NotImplementedException("The \"ISigner.AlgorithmName\" property is not implemented for the \"BeIdSigner\" class.");
private static byte[] PlainToDer(byte[] plain)
{
int valueLength = plain.Length / 2;
BigInteger r = new(1, plain, 0, valueLength);
BigInteger s = new(1, plain, valueLength, valueLength);
return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
}
}
This answer sums up the comments to the question.
iText and ECDSA signatures
First of all one has to realize that currently (i.e. for iText 7.2.2) ECDSA is not supported by all parts of the iText signing API.
This limitation is mostly due to the iText PdfPKCS7 class used to create and validate CMS signature containers embedded in PDFs. When it is used to create a signature container, the identifier it stores in the signatureAlgorithm field does not take the used hash algorithm into account. E.g. it uses the same value for RSA (with PKCS1 v1.5 padding) signatures, no matter if it's actually SHA1withRSA or SHA256withRSA or whichever combination.
For RSA this is ok because there indeed is an identifier that can be used for all these cases. For DSA it is somewhat ok because in many contexts DSA is limited to use with SHA1 only.
For ECDSA this is not ok, there only are identifiers taking the hash algorithm into account. iText uses the EC public key identifier in all these cases which is simply wrong. The reason why hardly anyone noticed this bug is that Adobe Acrobat validation apparently ignores the contents of this signatureAlgorithm field: You can even write the RSA identifier into this field of an ECDSA signature and the validation succeeds without an indication of a problem.
To create proper ECDSA signatures, therefore, one currently should not use the PdfPKCS7 class. As all the PdfSigner.signDetached methods internally use the PdfPKCS7 class, this in turn means that one must not use them but instead PdfSigner.signExternalContainer. As a consequence, one must not use an IExternalSignature implementation to retrieve one's signature value but instead an IExternalSignatureContainer implementation in which one builds the CMS signature container differently, for example using BouncyCastle classes.
In the case at hand the BeIDSignature implementation of IExternalSignature, therefore, must be replaced accordingly.
For further details please read the section Which Interface to Use of the iText knowledge base article Digital Signing with iText 7.
ECDSA signature formats
There are two major formats in which an ECDSA signature value can be stored, either as a TLV (DER) encoded sequence of two integers or (plain encoding) as the concatenation of fixed length representations of those two integers.
Depending on the used format one has to use specific algorithm identifiers for ECDSA and PLAIN-ECDSA respectively. If one needs a specific identifier, one can convert the signature value from one format to the other.
In the case at hand the Belgian ID card returns the ECDSA signature value in plain format. To use the more common non-PLAIN ECDSA identifiers, one has to convert that value to the DER format. This can be done using this method:
byte[] PlainToDer(byte[] plain)
{
int valueLength = plain.Length / 2;
BigInteger r = new BigInteger(1, plain, 0, valueLength);
BigInteger s = new BigInteger(1, plain, valueLength, valueLength);
return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
}
(X509Certificate2Signature helper method from the code examples of Digital Signing with iText 7)
Some thoughts:
Are you sure that there is an ECDSA key available on the chip ? I had a short look at the documentation (not sure it's up to date - cf. eid-mw github), which only mentions RSA. Additionally if you can sign using RSA/SHA256, having ECDSA support as well would mean that there's a second key pair on the card - I have some doubts about this ;
Try to sign with ECDSA / SHA384 in Adobe Reader using your eID - check whether you can validate the signature ;
Validate the signature online, using the SD-DSS tool: the diagnostic data may help you in pin-pointing what is wrong (e.g. a sha384 digest was generated, but the signature structure mentions sha256 as digest algo).

Decrypt JWT Token in C#

Hello I'm trying to see if they're any JWT token library similar to JOSE-JWT that also works on a Linux machine during runtime.
This is what I currently have in code that decrypts a token and grabs what I need.
private IamTokenDecrypted DecryptToken(string idToken)
{
byte[] key = WebEncoders.Base64UrlDecode(_ssoEncryptionKeyInfo.JsonWebKey.K);
string json = Jose.JWT.Decode(idToken, key, JweAlgorithm.DIR, JweEncryption.A256GCM);
var result = Jose.JWT.Payload(json);
var iamTokenDecrypted = JsonConvert.DeserializeObject<IamTokenDecrypted>(result);
return iamTokenDecrypted;
}
I have a security key and and as you see it has some encryption in it

How do you identify the tenant using a JWT when integrating with Autofac?

I'm trying to build a multi-tenant ASP.NET Core 2.1 WebApi.
I would like to chose tenant from the jwt token and not from the url or port.
So When user Request the token, I put it's tenant_id into the token.
But when I try to resolve the TenantId in the Autofac Multitenant strategy (ITenantIdentificationStrategy) like this:
public bool TryIdentifyTenant(out object tenantId)
{
_logger.LogInformation("***********************************TryIdentify");
tenantId = null;
try
{
var context = _httpContextAccessor()?.HttpContext;
if(context != null && context.Request != null)
{
var id = context.User.FindFirst("tenantId")?.Value;
if (id != null)
{
tenantId = id;
}
}
}
catch(Exception)
{
// Happens at app startup in IIS 7.0
}
return tenantId != null;
}
I see that the context.User is not jet populated and that's because the Jwt authentication didn't happen jet.
How to do it?
The short version of this is you don't get to do this for free. This is generally why it's best to use something you can trust but doesn't require additional support to use (like the host name in the request).
If you can only trust the token, then I have seen solutions that roughly do this (in pseudocode-that-looks-like-C#):
if(context.Items["tenant"] == null && context.User == null)
{
// no tenant has been identified before and
// token validation hasn't happened so manually
// get the tenant from the token knowing you are
// potentially getting an untrusted value.
context.Items["tenant"] = ManuallyLookAtToken();
}
else if(context.Items["tenant_trusted"] == null && context.User != null)
{
// a "trusted" tenant ID hasn't been read from the user
// principal so let's update.
context.Items["tenant"] = GetTenantFrom(context.User);
context.Items["tenant_trusted"] = true;
}
return context.Items["tenant"];
The risk in doing something like this is that you open yourself up to an attack where someone sends in a malformed token that only lives long enough to get past the initial part of your request pipeline. The token won't pass validation so the regular security should take care of it, but before that runs the tenant value isn't officially validated. If you have pipeline logic, that, for example... auto-provisions new tenants on first request or something like that?... then you may be in trouble. Someone could randomly generate millions of tenant names and kill your database. In cases like that you can sometimes fall back to other things like the host name.
Alternatively, you can actually manually invoke the token validation logic in that ManuallyLookAtToken() method and ensure it's valid before proceeding. It's sort of painful, but not impossible. That would mean technically you would run that twice during any given request, and it's sort of expensive, so consider the perf if you go that route and balance that with security implications.

Google Cloud Services put Fails when Using Signed URL

I am not able to PUT a file to google cloud services via a signed URL. When I try to do a PUT from a JS Client, I get:
"SignatureDoesNotMatch...The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method."
When I try to post the file using CURL, I get the same error.
The curl command I use is:
#!/bin/bash
URL="https://storage.googleapis.com/..."
echo $URL
curl $URL -H "Content-Type: image/jpg" --upload-file b.jpg
I have configured the bucket I intend to post data to based on the documentation, I have generated a service account with the key, and this key is used to generate the signed url.
The request I sign is of the form:
PUT
image/jpg
1234567890
my-bucket/b.jpg
where the expiration and bucket names are set and computed.
I have the following Groovy code to generate signed urls:
public String sign(PrivateKey key, String toSign) {
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(key);
signer.update(toSign.getBytes("UTF-8"));
byte[] rawSignature = signer.sign();
String s = new String(Base64.encodeBase64(rawSignature), "UTF-8");
return s;
}
public String signUrl(PrivateKey key, String clientId, String method, String md5, String contentType,
long expiration, String gcsPath) {
String toSign = "${method}\n${md5}\n${contentType}\n${expiration}\n${gcsPath}";
String signature = sign(key, toSign);
String url = java.net.URLEncoder.encode(signature);
return url;
}
public String generateSignedUrl(PrivateKey key, String clientId, String method, String md5, String contentType,
long expiration, String gcsPath) {
String canonicalizedResource = "/${gcsPath}";
String signature = signUrl(key, clientId, method, md5, contentType, expiration, canonicalizedResource);
String finalUrl = "https://storage.googleapis.com/${gcsPath}?GoogleAccessId=${clientId}&Expires=${expiration}&Signature=${signature}"
finalUrl
}
This code is accompanied with the following passing unit test lifted straight out of the gsutils github project (https://github.com/GoogleCloudPlatform/gsutil/blob/master/gslib/tests/test_signurl.py):
#Test
void thatWeCanSignAPutUrlCorrectly() {
String expected = """https://storage.googleapis.com/test/test.txt?GoogleAccessId=test#developer.gserviceaccount.com&Expires=1391816302&Signature=A6QbgTA8cXZCtjy2xCr401bdi0e7zChTBQ6BX61L7AfytTGEQDMD%2BbvOQKjX7%2FsEh77cmzcSxOEKqTLUDbbkPgPqW3j8sGPSRX9VM58bgj1vt9yU8cRKoegFHXAqsATx2G5rc%2FvEliFp9UWMfVj5TaukqlBAVuzZWlyx0aQa9tCKXRtC9YcxORxG41RfiowA2kd8XBTQt4M9XTzpVyr5rVMzfr2LvtGf9UAJvlt8p6T6nThl2vy9%2FwBoPcMFaOWQcGTagwjyKWDcI1vQPIFQLGftAcv3QnGZxZTtg8pZW%2FIxRJrBhfFfcAc62hDKyaU2YssSMy%2FjUJynWx3TIiJjhg%3D%3D""";
long expiration = 1391816302;
String signedUrl = gsUtils.generateSignedUrl(privateKey, "test#developer.gserviceaccount.com","PUT", "", "", expiration, "test/test.txt")
assertEquals(expected, signedUrl);
}
Thank you for whatever insights you may be able to provide, I have been at this problem for a while.
Debugging signed URL logic is difficult. There is a useful trick that helps, though. An error response like the one you describe will look like this:
<?xml version='1.0' encoding='UTF-8'?><Error><Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>
<StringToSign>PUT
text/jpeg
1472720161
/myBucket/test.txt</StringToSign></Error>
That last bit, <StringToSign>, is critical. The string in there is exactly the string that GCS will sign, and it's the string that you should also be signing. Compare you string against this one; it will probably be different in some way.
Also, because implementing this signing logic is tricky, the gcloud-java library has a signUrl() method that I recommend you use instead of implementing the logic yourself.
One reason that might cause this error (happened to me before) is when you generate a base64 encoded signature with your signed string, the encoded signature may contain illegal url characters + and /. Make sure you replace them in the string with %2B and %2F, respectively.

GCM Cloud Connection Server doesn't respond to SASL on XMPP

Using agsXMPP to connect to Google Cloud Messaging XMPP API for the purpose of sending notification to Android devices.
The connection is established OK, but on SASL start, after sending the PLAIN auth element, the server stops responding, and closes the connection after a further 20 seconds.
Base64 decoding the auth example from the documentation page (http://developer.android.com/google/gcm/ccs.html) shows login values of:
126200347933#projects.gcm.android.com12620034793#projects-ga-.android.comAIzaSyB3rcZNkfnqKdFb9mhzCBiYpORDA2JWWtw
Where as agsXMPP is (correctly I think) encoding the string, to give something like:
[ProjectID]\40gcm.googleapis.com[**API*KEY*PASSWORD**]
Note the \40 in my version instead of the # in the Google example - could this make a difference?
I'm expecting either a success or failure message, no response at all is difficult to debug. Could this at character be responsible for some failure, or does Google's implementation of XMPP just not provide the correct responses.
UPDATED:
I answered below, essentially, yes, Google can't handled the encoded # character because it doesn't support that XMPP extension.
After some more testing, I added a new SaslFactory mechanism in agsXMPP and bound it to use the username without encoding (part of extension http://xmpp.org/extensions/xep-0106.html, which Google doesn't support), and then on SaslStartEvent - specify that I want to use that mechanism instead of the inbuilt plain one. - and now the connection will continue normally.
xmpp = new XmppClientConnection();
xmpp.UseSSL = true;
xmpp.UseStartTLS = false;
xmpp.Server = "gcm.googleapis.com";
xmpp.ConnectServer = "gcm.googleapis.com";
xmpp.Port = 5235;
/* Other connection settings /*
SaslFactory.AddMechanism("MyPLAINMechanism", typeof(MyPlainMechanismClass));
xmpp.OnSaslStart += (sender, args) =>
{
args.Auto = false;
args.Mechanism = "MyPLAINMechanism";
args.ExtentedData = new GcmPlainSaslExtendedData
{
Username = "MY UNENCODED USERNAME"
};
};
Then we define the MyPlainMechanismClass which inherits from the Mechanism in agsXMPP, the source code is the same as the original PlainSaslMechanism except the line where the username is input - you can pass in an unencoded username using the ExtendedData property on args.
public class MyPlainMechanismClass: Mechanism
{
private XmppClientConnection m_XmppClient = null;
public GcmPlainSaslMechanism()
{
}
public override void Init(XmppClientConnection con)
{
m_XmppClient = con;
// <auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">$Message</auth>
m_XmppClient.Send(new agsXMPP.protocol.sasl.Auth(agsXMPP.protocol.sasl.MechanismType.PLAIN, Message()));
}
public override void Parse(Node e)
{
// not needed here in PLAIN mechanism
}
private string Message()
{
// NULL Username NULL Password
StringBuilder sb = new StringBuilder();
//sb.Append( (char) 0 );
//sb.Append(this.m_XmppClient.MyJID.Bare);
sb.Append((char)0);
//sb.Append(this.Username);
sb.Append(((GcmPlainSaslExtendedData) this.ExtentedData).Username);
sb.Append((char)0);
sb.Append(this.Password);
byte[] msg = Encoding.UTF8.GetBytes(sb.ToString());
return Convert.ToBase64String(msg, 0, msg.Length);
}
}
Our custom ExtendedData object which we use to pass in custom arguments, such as an unencoded username in this case.
public class GcmPlainSaslExtendedData : agsXMPP.Sasl.ExtendedData
{
public string Username { get; set; }
}