Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Password available in plain string format in virtual memory #3030

Open
2 of 5 tasks
Nikhil-in1abb opened this issue Mar 6, 2025 · 9 comments
Open
2 of 5 tasks

Password available in plain string format in virtual memory #3030

Nikhil-in1abb opened this issue Mar 6, 2025 · 9 comments
Assignees
Labels
enhancement API or feature enhancement

Comments

@Nikhil-in1abb
Copy link

Type of issue

  • Bug
  • Enhancement
  • Compliance
  • Question
  • Help wanted

Current Behavior

I am using a client application to connect with OPC UA server that supports username and password login.

In my client when i receive the input of the username and password I store it in an encrypted format.

when i try to connect to the server I use the below code

userIdentity = new UserIdentity(usernameStr, decryptedPasswordStr);

var session = Opc.Ua.Client.Session.Create(
configuration,
endpoint,
false,
false,
sessionName,
sessionTimeout,
userIdentity,
preferredLocales,
default(CancellationToken)).Result;

As it is shown I am making a local copy after decrypt in a string variable decryptedPasswordStr

This string is stored in memory in plaintext and it is in read only format. It will not be cleared. If I use tools like process hacker, I will be able to read the password in plain string text making it exposed and vulnerable.

Expected Behavior

Are there overloads to send password in encrypted format/byte format. if so I would request example code snippet to achieve the same.

Steps To Reproduce

No response

Environment

- OS:
- Environment:
- Runtime:
- Nuget Version:
- Component:
- Server:
- Client:

Anything else?

No response

@EthanChangAED EthanChangAED added the enhancement API or feature enhancement label Mar 6, 2025
@marcschier
Copy link
Contributor

@Nikhil-in1abb , you should be able to implement IUserIdentity to implement it in the way you want. Does that work?

@Nikhil-in1abb
Copy link
Author

Nikhil-in1abb commented Mar 7, 2025

@marcschier The IUseridentity class does not provide sufficient members to achieve my intended result.

I would want to use encoded/encrypted password to create new UserIdentity.

There is one other approach I tried. as shown below:

                    UserNameIdentityToken token = new UserNameIdentityToken();
                    token.UserName = usernameStr;
                    token.Password = Encoding.UTF8.GetBytes(DecryptedPasswordStr);
                    
                    userIdentity = new UserIdentity(token);

when I try to create session with this I am getting 'BadIdentityTokenInvalid' returned from the OPC UA server.

I would need an example/support to pass the token with password and use encoding/encryption instead of using
token.DecryptedPassword

@Nikhil-in1abb
Copy link
Author

Nikhil-in1abb commented Mar 10, 2025

Could not get solution for the issue, the stack is storing password as plaintext and creates multiple internal copy upon execution of create session with the userIdentity. There is no provision to use securestring as input argument as well.

Refer to the sample virtual memory read done with tools like process hacker exposing the password.

Image

@romanett
Copy link
Contributor

@Nikhil-in1abb Microsoft recommends not to use SecureString any more. (https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md).
Instead for environments with high security needs you could use Certificate or Token User Identities.

For securing the memory of the running process maybe virtualization-based security (VBS) could be an option.

What Method of storing /access passwords in the user identity token would you suggest?

@Nikhil-in1abb
Copy link
Author

@romanett, In my case my target OPC UA server is a legacy controller that supports only username/password user token policy.
so creating secure tokens like certificate based authentication/issued token is not an option.

There is an overload to use token to create UserIdentityToken (code section pasted above) where in I can use token.password which is byte array. Unlike string which is immutable in c# we could use byte array that can be overwritten/cleared upon execution.

when I try to execute create session with this I am getting 'BadIdentityTokenInvalid' returned from the OPC UA server.

I would need an example/support to pass the token with password instead of using token.DecryptedPassword

@romanett romanett self-assigned this Mar 11, 2025
@romanett
Copy link
Contributor

@Nikhil-in1abb thanks for the update.

The relevant code seems to be here:

private UserIdentityToken ValidateUserIdentityToken(

The Session implementation in the server is tightly bound to UserIdentityToken. You need to add a custom override for UserIdentityToken that stores the PW in a manner you need. And implemements encrypt / decrypt methods of UserIdentityToken.

As a short term solution I would suggest overriding the existing UserNameIdentityToken:

public partial class UserNameIdentityToken

As a longer term solution I would like to extend the existing UserNameIdentityToken in the stack with extra property / constructors to accept PW as byte array.

@romanett
Copy link
Contributor

romanett commented Mar 12, 2025

@Nikhil-in1abb I finally had time to debug it myself. You put the Passwort byte array into the field for the already encrypted password. The current encrypt method sees an empty password and therefore overrides m_password byte array.

@Nikhil-in1abb
Copy link
Author

Nikhil-in1abb commented Mar 12, 2025

@romanett , I had tried a work around by implementing the below override, this will copy the password bytes instead of decrypted password string. But still i see some password available in virtual memory I suspect either Utils.Append, or SecurityPolicies.Encrypt to make a internal copy of the byte array that is not handled properly.

I also do encrypt the password and decrypt it just before handing over to Utils.Append where it is sent as decrypted bytes array

Correct me if I am wrong, I would need guidance to solve the issue

public partial class ExtendedIdentityToken : UserNameIdentityToken
{
    public ExtendedIdentityToken(string username, byte[] password)
        : base()
    {
        UserName = username;
        Password = password;
        EncryptionAlgorithm = null;
    }
    public override void Encrypt(X509Certificate2 certificate, byte[] senderNonce, string securityPolicyUri)
    {
        if (Password == null || Password.Length == 0)
        {
            Password = null;
            return;
        }

        if (string.IsNullOrEmpty(securityPolicyUri) || securityPolicyUri == SecurityPolicies.None)
        {
            return; 
        }
        
        //byte[] dataToEncrypt = Utils.Append(Password, senderNonce);

        EncryptedData encryptedData = SecurityPolicies.Encrypt(
            certificate,
            securityPolicyUri,
            dataToEncrypt);

        Password = encryptedData.Data;
        EncryptionAlgorithm = encryptedData.Algorithm;

    }

@romanett
Copy link
Contributor

@Nikhil-in1abb i have to take a look at process explorer first how to dump passwords (never did that).

Please take a look at attached PR, does this solve the issue:

#3035

I have to take a look at SecurityPolicies.Encrypt or Utils.Append now.

In a new environment it could be preferable to not store the decrypted PW on the server at all and change the interface to just return the decrypted PW once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement API or feature enhancement
Projects
None yet
Development

No branches or pull requests

4 participants