I have been exploring Apache Shiro with Zeppelin and so far has been able to make authentication work with JdbcRealm but one thing that is not going well is giving the data source password as plain text.
Is there a way to avoid that?
My shiro.ini looks like:
[main]
dataSource = org.postgresql.ds.PGPoolingDataSource
dataSource.serverName = localhost
dataSource.databaseName = dp
dataSource.user = dp_test
dataSource.password = Password123
ps = org.apache.shiro.authc.credential.DefaultPasswordService
pm = org.apache.shiro.authc.credential.PasswordMatcher
pm.passwordService = $ps
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealmCredentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
jdbcRealm.dataSource = $dataSource
jdbcRealm.credentialsMatcher = $pm
shiro.loginUrl = /api/login
[roles]
admin = *
[urls]
/** = authc
Is there a way to avoid giving data source password as plain text
dataSource.password = Password123?
Would like to give something like:
$shiro1$SHA-256$500000$YdUEhfDpsx9KLGeyshFegQ==$m+4wcq4bJZo1HqDAGECx50LcEkRZI0zCyq99gtRqZDk=
yes, there is a way, but there will still be a password lying around somewhere due to the nature of shiro needing to know the password.
Why Hashing does not work
You posted
something like: $shiro1$SHA-256[…]
This is a hash, and thus it is not reversible. There is no way shiro could log into the datasource using this String.
Container managed datasources
The best approach I can recommend at this point is to have a container managed resource. A container is referring to a (web) application server in this case, like tomcat, OpenLiberty or Wildfly.
For your use case, try looking into the following:
extend org.apache.shiro.realm.jdbc.JdbcRealm or AuthorizingRealm
Add the JPA API to your module and inject a persistence context like so:
#PersistenceContext
EntityManager entityManager;
Override the methods
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
… to load from your managed datasource instead.
Drawbacks of this approach:
You just delegated datasource login to your container / application server. The server is still facing the same problems. E.g. with OpenLiberty, you will still need to store a master key of an encrypted (not hashed) password somewhere, and thus liberty will do exactly this.
use another configuration source
Instead of using a shiro.ini file, you could also write your own environment loader. You could request the file from an IP-restricted web service or a cryptographic hardware device.
Always a goal: restrict the environment
You should always restrict the environment.
E.g. create a user which can install, but not run your application and who cannot read the logs (called setup-user or so).
Create another user which can start the application, read but not modify configuration files and write logs, called a run-user.
Restrict access to configurations and logs for all other users on that system.
Getting involved
If you have other needs, feel welcome to discuss other solutions on the shiro mailing lists.
Related
I'm pretty new to Kerberos. I'm testing the Single Sign On feature using Kerberos. The environment: Windows clients (with Active Directory authentication) connecting to an Apache server running on Linux machine. The called cgi script (in Perl) connects to a DB server using the forwarded user TGT. Everything works fine (I have the principals, the keytab files, config files and the result from the DB server :) ). So, if as win_usr_a on Windows side I launch my CGI request, the CGI script connects to the remote DB and queries select user from dual and it gets back win_usr_a#EXAMPLE.COM.
I have only one issue I'd like to solve. Currently the credential cache stored as FILE:.... On the intermediate Apache server, the user running the Apache server gets the forwarded TGTs of all authenticated users (as it can see all the credential caches) and while the TGTs lifetime are not expired it can requests any service principals for those users.
I know that the hosts are considered as trusted in Kerberos by definition, but I would be happy if I could limit the usability of the forwarded TGTs. For example can I set the Active Directory to limit the forwarded TGT to be valid only to request a given service principal? And/Or is there a way to define the forwarded TGT to make it able to be used only once, namely after requesting any service principal, become invalid. Or is there a way the cgi script could detect if the forwarded TGT was used by someone else (maybe check a usage counter?).
Now I have only one solution. I can define the lifetime of the forwarded TGT to 2 sec and initiate a kdestroy in the CGI script after the DB connection is established (I set that the CGI script can be executed by the apache-user, but it cannot modify the code). Can I do a bit more?
The credential caches should be hidden somehow. I think defining the credential cache as API: would be nice, but this is only defined for Windows. On Linux maybe the KEYRING:process:name or MEMORY: could be a better solution as this is local to the current process and destroyed when the process is exited. As I know apache create a new process for a new connection, so this may work. Maybe KEYRING:thread:name is the solution? But - according to the thread-keyring(7) man page - it is not inherited by clone and cleared by execve sys call. So, if e.g. Perl is called by execve it will not get the credential cache. Maybe using mod_perl + KEYRING:thread:name?
Any idea would be appreciated! Thanks in advance!
The short answer is that Kerberos itself does not provide any mechanism to limit the scope of who can use it if the client happens to have all the necessary bits at a given point in time. Once you have a usable TGT, you have a usable TGT, and can do with it what you like. This is a fundamentally flawed design as far as security concerns go.
Windows refers to this as unconstrained delegation, and specifically has a solution for this through a Kerberos extension called [MS-SFU] which is more broadly referred to as Constrained Delegation.
The gist of the protocol is that you send a regular service ticket (without attached TGT) to the server (Apache) and the server is enlightened enough to know that it can exchange that service ticket to itself for a service ticket to a delegated server (DB) from Active Directory. The server then uses the new service ticket to authenticate to the DB, and the DB see's it's a service ticket for win_usr_a despite being sent by Apache.
The trick of course is that enlightenment bit. Without knowing more about the specifics of how the authentication is happening in your CGI, it's impossible to say whether whatever you're doing supports [MS-SFU].
Quoting a previous answer of mine (to a different question, focused on "race conditions" when updating the cache)
If multiple processes create tickets independently, then they have no
reason to use the same credentials cache. In the worst case they would
even use different principals, and the side effects would be...
interesting.
Solution: change the environment of each process so that KRB5CCNAME
points to a specific file -- and preferably, in an
application-specific directory.
If your focus in on securing the credentials, then go one step further and don't use a cache. Modify your client app so that it creates the TGT and service tickets on-the-fly and keeps it private.
Note that Java never publishes anything to the Kerberos cache; it may either read from the cache or bypass it altogether, depending on the JAAS config. Too bad the Java implementation of Kerberos is limited and rather brittle, cf. https://steveloughran.gitbooks.io/kerberos_and_hadoop/content/sections/jdk_versions.html and https://steveloughran.gitbooks.io/kerberos_and_hadoop/content/sections/jaas.html
We are using Play 2.1.1 and its built-in JPA integration (JPA.em()
etc).
How can we dynamically change the db.pass property? Play.application().configuration() seems
to be immutable as of Play 2.1. (or we're at least not aware of the mutators)
If we are able to change db.pass, how can we reload the DB configuration so that JPA.em() returns an EntityManager using the new password?
What we are trying to avoid is having to recreate the EntityManager using
EntityManagerFactory. We want to continue to let Play manage that in
the JPA helper class.
Background
The system has a default DB configuration for running locally. When deployed to a server, the DB password is dynamically set on the running application using the following script:
#!/bin/bash
stty -echo
read -p "Password: " PASS
stty echo
curl -k https://127.0.0.1:8443/someUrl/pwd --data "password=$PASS"
The application receives this data and then recreates the Hibernate
SessionFactory. Our new Play app will be required to do something
similar.
The key is to use the ConfigFactory to create a new Config entry. This new Config contains an entry for password with the value coming from your http call to your password service.
A new Configuration is created using the new Config, which in turn falls back to the original Config from the original Configuration.
Basically the new password entry supersedes the original.
It sound long winded when you say it, but the code is pretty readable.
public class Global extends GlobalSettings {
// inject http client to make call for password
#Override
public Configuration onLoadConfig(Configuration configuration, File file, ClassLoader classLoader) {
final Config config = ConfigFactory.parseString(String.format("db.default.user=%s", callPasswordService()));
return new Configuration(config.withFallback(configuration.getWrappedConfiguration().underlying()));
}
}
To answer my own question, at first we solved the problem of updating the immutable configuration at runtime by overriding Configuration.onLoadConfig with the following:
If configuration indicates that production.level is PROD
Read the password from stdin
Create a new configuration by converting the old one to a map and building a new one with ConfigFactory.parseMap, with the new parameter as well
Return super.onLoadConfig
However, this still didn't address that the problem of reloading the DB configuration. In the end, my colleague created a Play! plugin which essentially a copy of some JPA classes with the added capability of being reloaded with a Map of configuration properties.
Update
The "hook" is the additional static method which the plugin adds to the JPA class (e.g. reloadWithProperties). This method creates a new data source which is then rebound in JNDI.
I am using ReportViewer to show the reports on my windows WPF application using .Net 4.0. These reports are deployed on a separate SSRS 2008 report server and not the local machine. Right now, I am passing the credentials of the server in the following manner:
string userName = configClient.Settings.Get("UserName").Value.ValueXml.InnerText;
string password = configClient.Settings.Get("Password").Value.ValueXml.InnerText;
string domain = configClient.Settings.Get("Domain").Value.ValueXml.InnerText;
IReportServerCredentials irsc = new ReportViewerCredentials(userName, password, domain);
_reportViewer.ServerReport.ReportServerCredentials.NetworkCredentials = irsc.NetworkCredentials;
Also, I am using the following settings with the ReportViewer if it is of any use:
_reportViewer.ProcessingMode = ProcessingMode.Remote;
_reportViewer.ShowParameterPrompts = false;
_reportViewer.ServerReport.ReportServerUrl = new Uri(Properties.Settings.Default.ReportServer);
_reportViewer.ServerReport.ReportPath = Properties.Settings.Default.Reports;
I am using the config file to save and retrieve the credentials for the server access, but I do not think this is a secure way of doing it. I would like to implement this in a secure way where I do not need to take the credentials from the user or from the config file. Both the local machine and the server would be on the same network.
I am not sure how to do it, can this be done through impersonation, I am just guessing as I do not have much idea about security and impersonation. Also, if it can be done, can I get a sample or may be a link to an article through which I can get this done.
The core idea is to avoid storing the username and password on the client. I searched for solution but what I got was very vague in nature.
Please do not close this thread as it is an open question, but it is important for me, as I am approaching deadline and working on too many things at a time. Sorry for inconvenience caused if any.
I have found a way to not pass the credentials at all and still be able to retrieve the reports from the Reports Server, but not without help.
I have configured a new Role assignment in the Report Manager using the URL http://localhost/Reports/Pages/Folder.aspx. Go to Properties tab, and add a New Role Assignment and add Everyone and provide the required right to it.
This way, I would not need to pass any credentials from the client for the ReportViewer. Also, it can be used to configure the access for a selected users.
I would like to create a C# WinForms application which will create web requests to servers that require username and password (Basic authentication).
I want to save the password locally (encrypted of course) on the machine that runs the application.
Basically, I need something that operates as Keychain in iOS, or "secure storage" in eclipse, only for .NET
I've found the Credential Manager in Windows, but all API examples are from .NET Framework V2.0
Isn't there a normal API from Microsoft about how to use it in C#?
Windows applications can use DPAPI for that, the Data Protection API, which stores secrets on a computer, encrypted (indirectly) with a user's login credentials. Any process that runs with the user's privileges can access this data. Optionally you can let the user add his own password (for this one item / secret) if you like (PrompStruct). The DPAPI is also accessible via C# (there are examples on the net).
You can store the credentials in a section in app.config and then encrypt the section (similar to what you'll do in web.config for a web application).
You can use SectionInformation.ProtectSection to protect the section, and SectionInformation.GetRawXml to retrieve the encrypted info (decryption is done transparently).
Example (taken from the MSDN article below):
static public void ProtectSection()
{
// Get the current configuration file.
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
// Get the section.
UrlsSection section =
(UrlsSection)config.GetSection("MyUrls");
// Protect (encrypt)the section.
section.SectionInformation.ProtectSection(
"RsaProtectedConfigurationProvider");
// Save the encrypted section.
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
// Display decrypted configuration
// section. Note, the system
// uses the Rsa provider to decrypt
// the section transparently.
string sectionXml =
section.SectionInformation.GetRawXml();
Console.WriteLine("Decrypted section:");
Console.WriteLine(sectionXml);
}
http://msdn.microsoft.com/en-us/library/system.configuration.sectioninformation.protectsection.aspx
I have a C++ application that makes a HTTPS connection to one of our servers.
In my ideal world, I would like the following to occur:
App Starts
App makes Windows trust the server's root CA (no GUI please, just system calls)
App talks to server, does its work, etc.
App makes windows forget about the server's root CA
done
I do NOT want this root CA to necessarily be trusted by other apps. Therefore I don't want to install the cert system-wide.
I also would like it if the user did not need Admin privileges.
My initial plan was to create an in-memory (CERT_STORE_PROV_MEMORY) store, add my cert to that, then add that in-memory store to the system store using CertAddStoreToCollection.
While all the CryptoAPI function calls succeed, WinHttp does not like it.
Here is the skeleton of what I'm doing - perhaps someone knows a trick?
Or perhaps this is wrong-headed in the first place?
hMemStore = CertOpenStore(CERT_STORE_PROV_MEMORY, ...);
pCert = CertCreateCertificateContext(..., pCertBytes, ...);
CertAddCertificateContextToStore(hMemStore, pCert, ...);
hRootStore = CertOpenSystemStore(NULL, "ROOT");
CertAddStoreToCollection(hRootStore, hMemStore, ...);
// Then later on...
WinHttpSendRequest(...)
A few notes:
Everything works when I use WinHttp's SECURITY_FLAG_IGNORE_UNKNOWN_CA, so I'm fairly sure this really is the issue.
I have already seen this SO question - it is close, but does not address the issue of making the cert only temporarily trusted, while the app runs.
Thanks!
Since you don't want other applications to trust this cert, you need to do part of the certificate validation yourself. Disable the CA check with the option SECURITY_FLAG_IGNORE_UNKNOWN_CA and then get the call back for connecting to the server WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER. In that callback fetch the cert with WINHTTP_OPTION_SERVER_CERT_CONTEXT and do your validation. Cancel/Close the request if it's not who you want, continue the request if it's correct.