JAAS - malicious subject and principal creation - security

I'm a newbie to JAAS and I still can't get one thing: what if a malicious user creates the subject and principal manually ?
Shouldn't there be some validation of subject/principal in case the user has tampered with it ? None of the tutorials I've seen even mentions this.
Look at this example (ch02 from jaasbook.com):
SimpleCallbackHandler cb = new SimpleCallbackHandler(username,
password);
LoginContext ctx = new LoginContext("chp02", cb);
ctx.login();
Subject subject = ctx.getSubject();
System.out.println("Logged in " + subject);
Subject.doAsPrivileged(subject, new PrivilegedAction() { ...
And if I replace this code with this one:
Subject subject = new Subject();
Principal p = new SysAdminPrincipal(username);
subject.getPrincipals().add(p);
Subject.doAsPrivileged(subject, new PrivilegedAction() { ...
it works just as well (at least in this sample code).
I must have missed sth obvious otherwise it would make no sense to use jaas at all.
Thanks

The distinction is between trusted and untrusted code.
If you allow code to run within the JVM that you do not trust then you need to protect the JVM with a configured Policy and running with a SecurityManager enabled. This is akin to the Java Applet environment. Within such an environment you will typically lock down parts of the codebase such that only code from trusted sources, and optionally cryptographically signed, can run, or call, other parts of code.
Within Java security, when a permission check is made the entire calling stack is checked to ensure that every part of the stack is allowed to use the permission.
In this case, you are starting a PrivilegedAction which associates a Subject to the AccessControlContext. If you look into the sourcecode you'll see that performs a permission check:
javax.security.auth.AuthPermission "doAs";
In the default Java security policy, the only code which can do this are installed Java extensions, so if you want to do this in your own code, with a running SecurityManager, you will need to manually set this permission.
So, the Subject class is unprotected because it is of no affect unless its associated with an AccesssControlContext.
The AccesssControlContext is protected by an AuthPermission.
The setting of AuthPermission is protectectd by the security Policy and SecurityManager, which are configured at JVM startup.
In this case, if you run with no SecurityManager, or a custom Policy which grants an AllPermission to any codebase, and you allow untusted code to run, then you have a big security problem.
If you want to run untrusted code and assign Subjects to AccessControlContexts then protect the code which does this will something like the following policy file:
grant codebase "file://home/me/myapp.jar" signedby "me" {
javax.security.auth.AuthPermission "doAs";
}
This protects that only your codebase, including the call stack, can perform the Subject assignment.
Or just make sure that you trust the code you deploy to your app...this is the 99% deployment scenario where most applications are protected within their host environment and do not allow remote code to be executed.

Related

Automating SharePoint scripts/code with LegacyAuthProtocolsEnabled set to false

We use the Microsoft.SharePoint.Client library to automate SharePoint work from our workflow engine but yesterday, one of our client informed us they wanted to disable the Legacy Authentication (LegacyAuthProtocolsEnabled to false).
Once I tried it on our end, I ended up getting an Unauthorised exception.
All in good wanting to disable the Legacy Authentication for obvious security reason, but the problem with the Modern Authentication is that it requires user interaction which is clearly not a solution since we are running tasks in the background.
I've been googling this for quite some time but I haven't found a solution as of yet on how to handle automatic authentication for background work.
Is there a way to "authenticate" to SharePoint without any user interaction while LegacyAuthProtocolsEnabled is set to false?
I found an article that suggested using the App Authentication but after reading more about it, I believe this is considered an old method to authenticate and is likely to be deprecated as well over time, but I thought I'd still give it a go just in case but it did not work. When I got to
https://tenant.sharepoint.com/_layouts/15/appregnew.aspx
Where tenant is our company domain name, and I click on the "Create" button after filling in all the relevant fields, I get the following error, which is completely useless:
Sorry, something went wrong
An unexpected error has occurred.
TECHNICAL DETAILS
According to this article HOW TO HARDEN YOUR SHAREPOINT ONLINE ENVIRONMENT BY DISABLING LEGACY AUTHENTICATION, Legacy Authentication was no longer be an option as of the 13/10/2020, yet here we are, and the option is still available in SharePoint 365 and while the article is interesting explain why Legacy Authentication should be switched off, etc... it does not get into any details as to how automated solutions should be handled.
Also found an old thread "LegacyAuthProtocolsEnabled" and Scripted Logons to SharePoint Online? where #DeanWang suggests leaving it turned on as:
All custom CSOM, PowerShell code will stop working
This may also prevent third-party apps from accessing SharePoint
Online resources.
I'm going to stop here as I could keep going and the question is already too long for my liking and bottom line is, does anyone know if there is a way, and what is the best way, to authenticate to SharePoint while running automated "scripts/code" from a background task without requiring any user interaction while the Legacy Authentication is switch off?
Thanks
Update-1
After reading articles after articles, I've yet to connect to SharePoint 365.
I also spend more time on the PnP Framework as recommended by numerous articles. I created a dummy app with the following sample code which is used again in various articles, including this one:
Secure Authentication of SharePoint with PnP Framework with C#(Code)
My code is identical as you can see:
var clientContext = new AuthenticationManager().GetACSAppOnlyContext(
"https://mycompany.sharepoint.com/sites",
"MyClientid",
"MySecretId");
using (clientContext)
{
//Get Lists
var web = clientContext.Web;
var lists = web.Lists;
clientContext.Load(lists);
clientContext.ExecuteQuery();
foreach (var list in lists)
{
}
}
And even though I've granted full control in Azure for the specific test app that's using the specific ClientId and SecretId
I'm still getting the following error (401 - unauthorized):
System.Exception
HResult=0x80131500
Message=Token request failed.
Source=PnP.Framework
StackTrace:
at SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2.OAuth2S2SClient.Issue(String securityTokenServiceUrl, OAuth2AccessTokenRequest oauth2Request) in /_/src/lib/PnP.Framework/Utilities/OAuth/OAuth2S2SClient.cs:line 18
at PnP.Framework.Utilities.TokenHelper.GetAppOnlyAccessToken(String targetPrincipalName, String targetHost, String targetRealm) in /_/src/lib/PnP.Framework/Utilities/TokenHelper.cs:line 116
at PnP.Framework.Utilities.ACSTokenGenerator.GetToken(Uri siteUrl) in /_/src/lib/PnP.Framework/Utilities/ACSTokenGenerator.cs:line 37
at PnP.Framework.AuthenticationManager.<GetContextAsync>b__59_0(String site) in /_/src/lib/PnP.Framework/AuthenticationManager.cs:line 971
at PnP.Framework.AuthenticationManager.<>c__DisplayClass75_0.<GetAccessTokenContext>b__0(Object sender, WebRequestEventArgs args) in /_/src/lib/PnP.Framework/AuthenticationManager.cs:line 1336
at Microsoft.SharePoint.Client.ClientRuntimeContext.OnExecutingWebRequest(WebRequestEventArgs args)
at Microsoft.SharePoint.Client.ClientContext.FireExecutingWebRequestEventInternal(WebRequestEventArgs args)
at Microsoft.SharePoint.Client.ClientContext.GetWebRequestExecutor()
at Microsoft.SharePoint.Client.ClientContext.GetFormDigestInfoPrivate()
at Microsoft.SharePoint.Client.ClientContext.EnsureFormDigest()
at Microsoft.SharePoint.Client.ClientContext.ExecuteQuery()
at ConsoleApp5.Program.Main(String[] args) in C:\Users\myuser\source\repos\ConsoleApp5\ConsoleApp5\Program.cs:line 23
This exception was originally thrown at this call stack:
[External Code]
SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2.OAuth2WebRequest.GetResponse() in OAuth2WebRequest.cs
SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2.OAuth2S2SClient.Issue(string, SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2.OAuth2AccessTokenRequest) in OAuth2S2SClient.cs
Inner Exception 1:
WebException: The remote server returned an error: (401) Unauthorized.
Is there another section I should be looking at (and change) in the App Registration in Azure
Since it's the SharePoint Online that we are talking about, one easy way to connect to different SharePoint Sites is by using the Azure AD App-Only approach and since you are talking about a Deamon Service you can easily use Application Permissions when registering the App Registration.
You can, and you should, read more about it from the linked Microsoft Docs article.
You can also loggin via certificate or app registration secret as it is discribed in the Log in to Microsoft 365 in order to create automated CI CD SPFx pipelines, for example.
Hope the above helps, if not feel free to ask :)
Update: Please read below in order to have a better understanding.
Firstly, in your code segment you are using a wrong method from the PnP.Framework package.
AuthenticationManager().GetACSAppOnlyContext()
The above method refers to a completely different method of obtaining an authentication token, more specifically the Sharepoint App-Only model, which... well.... more or less is not being used nowadays quite so ofte. I think I read somewhere that MS is thinking of retiring this kind of Authentication and going onwards on the path of Azure Active Directory authentication, but, unfotunately, I cannot seem to find the link.
Furthermore, I have collected three projects and uploaded them to github for you to see. You can simply clone the repo and run the projects as-is from HERE.
As you will be able to see for yourself, there are three projects in the solution, which you can run each one individually from VSCode or Vs.
More in detail:
ConsoleApp1
(sorry for the name but forgot to switch it :) )
This is a Deamon Console Project that references the PnP.Framework namespace and tries to utilize all of the goodies that the good folks form the PnP Community have contributed.
The procedure is straight forward and is the same for all three projects ->
Read the AppConfiguration
Request the Access Token with appropriate scopes (Depending the service that i am referencing)
Declare the Token to be used by our Client Context.
In the PnP.Framework-related project the above cycle can be seen as below
AuthenticationConfiguration config = AuthenticationConfiguration.ReadFromJsonFile("appsettings.json");
var authManager = new PnP.Framework.AuthenticationManager(config.ClientId, config.Certificate.CertificateDiskPath, config.Certificate.CertificatePassword, config.Tenant);
using (var cc = authManager.GetAccessTokenContext("https://<REPLACE:name of tenant>.sharepoint.com/sites/testsite2", (string siteURL) => authManager.GetAccessToken(siteURL)))
ConsoleAppMSGraph
As the name suggests this Deamon Console App utilizes GraphServiceClient graphClient in order to get all the information that you request through the graph endpoint.
Subsequntly, you will notice that for this porject the scope name changes to
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
In addition, we request a collection of all the lists that currently reside in our SharePoint Root Site with the below segment:
var lists = await graphClient.Sites["root"].Lists
.Request()
.GetAsync();
ConsoleAppSPClient
This app is the default and most simple way of accessing data on Sharepoint.
The projects utilizes MSAL.Net and Microsoft.Sharepoint.Client namespaces in order to fetch an access token and, subsequently, embed that token in all our next requests.
In order to keep the answer a bit short, please refer to here in order to see how we initiate a Confidential App Client, request for a token and, later on, embedd it in our ClientContext object.
Notes
I have listed in the Readme.md of the repo, which permissions you should give to your app registration. You can view them Here.
I am using the Sites.FullControl.All but you can narrow down the list of sites that the app registration will have access by using the Sites.Selected.
All of the above projects, reference a common class library that serves as a strongly typed configuration object.
IMPORTANT you should always use a certificate to authenticate the client app as it is mentioned here. The previous link also describes the way you can create a certificate and upload it to the store of the app registration.
Amazing! Thank you very much #Jimas13. For the last 2 weeks I was struggling to find solution to my problem!! You saved me!! If you ever been in Greece let me buy you a drink!

Node typescript library environment specific configuration

I am new to node and typescript. I am working on developing a node library that reaches out to another rest API to get and post data. This library is consumed by a/any UI application to send and receive data from the API service. Now my question is, how do I maintain environment specific configuration within the library? Like for ex:
Consumer calls GET /user
user end point on the consumer side calls a method in the library to get data
But if the consumer is calling the user end point in test environment I want the library to hit the following API Url
for test http://api.test.userinformation.company.com/user
for beta http://api.beta.userinformation.company.com/user
As far as I understand the library is just a reference and is running within the consumer application. Library can for sure get the environment from the consumer, but I do not want the consumer having to specify the full URL that needs to be hit, since that would be the responsibility of the library to figure out.
Note: URL is not the only problem, I can solve that with environment switch within the library, I have some client secrets based on environments which I can neither store in the code nor checkin to source control.
Additional Information
(as per jfriend00's request in comments)
My library has a LibExecutionEngine class and one method in it, which is the entry point of the library:
export class LibExecutionEngine implements ExecutionEngine {
constructor(private environment: Environments, private trailLoader:
TrailLoader) {}
async GetUserInfo(
userId: string,
userGroupVersion: string
): Promise<UserInfo> {
return this.userLoader.loadUserInfo(userId, userGroupVersion)
}
}
export interface ExecutionEngine {
GetUserInfo(userId: string, userGroupVersion: string): Promise<UserInfo>
}
The consumer starts to use the library by creating an instance of the LibraryExecution then calling the getuserinfo for example. As you see the constructor for the class accepts an environment. Once I have the environment in the library, I need to somehow load the values for keys API Url, APIClientId and APIClientSecret from within the constructor. I know of two ways to do this:
Option 1
I could do something like this._configLoader.SetConfigVariables(environment) where configLoader.ts is a class that loads the specific configuration values from files({environment}.json), but this would mean I maintain the above mentioned URL variables and the respective clientid, clientsecret to be able to hit the URL in a json file, which I should not be checking in to source control.
Option 2
I could use dotenv npm package, and create one .env file where I define the three keys, and then the values are stored in the deployment configuration which works perfectly for an independently deployable application, but this is a library and doesn't run by itself in any environment.
Option 3
Accept a configuration object from the consumer, which means that the consumer of the library provides the URL, clientId, and clientSecret based on the environment for the library to access, but why should the responsibility of maintaining the necessary variables for library be put on the consumer?
Please suggest on how best to implement this.
So, I think I got some clarity. Lets call my Library L, and consuming app C1 and the API that the library makes a call out to get user info as A. All are internal applications in our org and have a OAuth setup to be able to communicate, our infosec team provides those clientids and secrets to individual applications, so I think my clarity here is: C1 would request their own clientid and clientsecret to hit A's URL, C1 would then pass in the three config values to the library, which the library uses to communicate with A. Same applies for some C2 in the future.
Which would mean that L somehow needs to accept a full configuration object with all required config values from its consumers C1, C2 etc.
Yes, that sounds like the proper approach. The library is just some code doing what it's told. It's the client in this case that had to fetch the clientid and clientsecret from the infosec team and maintain them and keep them safe and the client also has the URL that goes with them. So, the client passes all this into your library, ideally just once per instance and you then keep it in your instance data for the duration of that instance

How can I sign a JWT token on an Azure WebJob without getting a CryptographicException?

I have a WebJob that needs to create a JWT token to talk with an external service. The following code works when I run the WebJob on my local machine:
public static string SignES256(byte[] p8Certificate, object header, object payload)
{
var headerString = JsonConvert.SerializeObject(header);
var payloadString = JsonConvert.SerializeObject(payload);
CngKey key = CngKey.Import(p8Certificate, CngKeyBlobFormat.Pkcs8PrivateBlob);
using (ECDsaCng dsa = new ECDsaCng(key))
{
dsa.HashAlgorithm = CngAlgorithm.Sha256;
var unsignedJwtData = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(headerString)) + "." + Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(payloadString));
var signature = dsa.SignData(Encoding.UTF8.GetBytes(unsignedJwtData));
return unsignedJwtData + "." + Base64UrlEncoder.Encode(signature);
}
}
However, when I deploy my WebJob to Azure, I get the following exception:
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: NotificationFunctions.QueueOperation ---> System.Security.Cryptography.CryptographicException: The system cannot find the file specified. at System.Security.Cryptography.NCryptNative.ImportKey(SafeNCryptProviderHandle provider, Byte[] keyBlob, String format) at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, CngKeyBlobFormat format, CngProvider provider)
It says it can't find a specified file, but the parameters I am passing in are not looking at a file location, they are in memory. From what I have gathered, there may be some kind of cryptography setting I need to enable to be able to use the CngKey.Import method, but I can't find any settings in the Azure portal to configure related to this.
I have also tried using JwtSecurityTokenHandler, but it doesn't seem to handle the ES256 hashing algorithm I need to use (even though it is referenced in the JwtAlgorithms class as ECDSA_SHA256).
Any suggestions would be appreciated!
UPDATE
It appears that CngKey.Import may actually be trying to store the certificate somewhere that is not accessible on Azure. I don't need it stored, so if there is a better way to access the certificate in memory or convert it to a different kind of certificate that would be easier to use that would work.
UPDATE 2
This issue might be related to Azure Web Apps IIS setting not loading the user profile as mentioned here. I have enabled this by setting WEBSITE_LOAD_USER_PROFILE = 1 in the Azure portal app settings. I have tried with this update when running the code both via the WebJob and the Web App in Azure but I still receive the same error.
I used a decompiler to take a look under the hood at what the CngKey.Import method was actually doing. It looks like it tries to insert the certificate I am using into the "Microsoft Software Key Storage Provider". I don't actually need this, just need to read the value of the certificate but it doesn't look like that is possible.
Once I realized a certificate is getting inserted into a store somewhere one the machine, I started thinking about how bad of a think that would be from a security standpoint if your Azure Web App was running in a shared environment, like it does for the Free and Shared tiers. Sure enough, my VM was on the Shared tier. Scaling it up to the Basic tier resolved this issue.

How to set Parse Installation Class security (class-level permissions and/or ACL)?

I'm developing a Parse App and currently checking the backend security. I'm a bit lost regarding the Installation Class permissions. It is (by default) readable and writable by everyone. Thus, any user could delete every object of the class.
My question is: is it protected by default like the User class? Or should I add ACL for every new registration to push notifications? Or change the class level permissions?
Many thanks for your help,
Parse defaults to public read/write access for everything outside of User to streamline development.
Security measures will vary from one app to another depending on use-case, but assuming that you have associated each Installation to a User, I would highly recommend applying an ACL which gives public read and limits writes to the specific user.
In case you are not already associating each Installation to a User, here's a nice piece of cloud code to take care of it for you.
Parse.Cloud.beforeSave(Parse.Installation, function(request, response) {
Parse.Cloud.useMasterKey();
if (request.user) {
request.object.set('user', request.user);
} else {
request.object.unset('user');
}
response.success();
});
It's a good place to start by creating ACLs which provide public read and user-specific write access. That one step alone will drastically improve security.

Multiple Auth drivers in kohana3.2

I'm working on a project where I'm trying to implement authentication against external user base for customers, this seems to be working correctly.
Recently there has been added another requirement that some people (not present in the aforementioned base) need to be able to edit parts of pages' content. First thing that comes to mind is to have separate ORM/File Auth driver enabled for those few editors to be able to authenticate them separately.
Is it possible to use two Auth drivers at the same time in Kohana 3.2?
Yes, you can use different drivers at once. Just create another instance instead of standard singleton:
// default Auth
$config = Kohana::$config->load('auth');
$auth = new Auth($config);
$user = $auth->get_user();
// special Auth for administration
$config2 = Kohana::$config->load('admin_auth');
$auth2 = new Auth($config2);
$admin = $auth2->get_user();
Restrictions:
You must use differ configs (driver and session_key values must differ). Note that some settings are defined in classes and cant be changed by config (for example, "remember" cookie named authautologin).
You cant share default ORM models (Model_User, Model_Token, Model_Role), because their names are hardcoded. But ORM driver & File driver can be used.
Kohana's Auth module does not natively support using two Drivers.
However, you can implement a new Driver yourself very easily. You can follow the instructions for creating a new Driver by copying the existing driver and modifying it, here: http://kohanaframework.org/3.3/guide/auth/driver/develop
The simple thing to do would be to put the following logic in your _login method:
Check the external user database for a valid login
If there is a valid user in the external user database, return true.
If there is no valid user in the external user database, check the local user database instead.
If the user exists in the local database, return true.

Resources