Using PAT to authenticate your tools

One of the strength point of VSTS / TFS is the extensibility through API, and now that we have a really nice set of REST API, it is quite normal to write little tools that interacts with your VSTS / TFS instances.

Whenever you write tools that interact with VSTS / TFS you need to decide how to authenticate to the server. While for TFS is quite simple because you can simply run the tool with Active Directory user and use AD integration, in VSTS integrating with your AD requires more work and it is not always a feasible solution.

Actually the best alternative is to use Personal Access Tokens to access your server even if you are using TFS and you could use AD authentication.

PAT acts on behalf of a real user

You can generate Personal Access Token from security section of your user profile, and this gives you immediately idea that the token is related to a specific account.

image

Figure 1: Accessing security information for your profile

From Personal access tokens section of your profile you can generate tokens to access server on behalf of your user. This means that the token cannot have more rights that your user have. This is interesting because if you revoke access to a user, all PATs related to that user are automatically disabled, also, whatever restriction you assign to the user (ex deny access to some code path), it is inerently applied to the token.

PAT expires in time

You can see from point 1 of Figure 2 that the PAT has an expiration (maximum value is 1 year) and this imply that you have no risk of forgetting some tool authenticated somewhere during years.

This image shows how to create a PAT, and point out that the token expires, is bound to a specific account and you can restrict permission of the PAT to given area.

Figure 2: PAT Creation page in VSTS

A tipical security problem happens when you create in your TFS / VSTS a user to run tools, such as TFSTool or similar one. Then you use that user in every tool that need to do unattended access your TFS instance and after some years you have no idea how many tools are deployed that have access to your server.

Thanks to PAT you can create a different PAT for each tool that need to unattendely authenticate to your server, after one year maximum the tool will lose authentication and it need to have a new Token. This will automatically prevent  the risk of having old tools that after year still have access to your data even if they are not actively used anymore.

For VSTS (point 2) you should also specify the account that the PAT is able to access if your user have rights to access more than one account.

PAT Scope can be reduced

In Figure 2 the point 3 highlight that you can restrict permission of PAT based on TFS / VSTS area. If your tool need to manipulate work items and does not need to access code or other area of TFS, it is a best practice to create the token and give access only to Work Items. This means that, even if the user can read and write code, the token can have access only to Work Item.

Another really important aspect is that many areas have the option to specify access in read-only mode. As an example, if your tool needs only to access Work Items to create some reports, you can give PAT only Work Item (read) access, so the tool will be able to access only Work Item in read-only way.

The ability to reduce the surface of data that can be accessed by a PAT  is probably the number one reason to use PAT instead of  AD authentication for on-premise TFS.

PAT can be revoked

Any PAT can be revoked any time with a single click. This means that if you use the pattern of one PAT for each tool you can selectively revoke authentication to any tool revoking associated PAT. This capability is really interesting for on-premise TFS, because if you want to selectively revoke access to specific tool without PAT, you need to use a different user for each different tool and disable that specific user.

Conclusion

Using PAT is not only useful if you want to create token used by tools that need to do an unattended authentication to the server, but you can use PAT even for tools that you use, if you want to be sure that the tool will not have access to certain part of your account (you can use a PAT that can only access code to use with Git tools), or if the tool does not support MSA or AAD authentication.

How to add a user to Project Collection Service Account in TFS / VSO

VSO and TFS have a special group called: Project Collection Service Account that has really powerful permission, and usually no user should be part of that group. There are specific circumstances, like running TFS Integration platform to move code to TFS, where the account used to access VSO needs to be part of this group to temporary have special permission.

Sadly enough, the UI does not allow you to directly add a user to that group, because the add button is disabled if you select that group.

image 

Figure 1: You cannot add users or group to Project Collection Service Account Users directly from the ui.

The reason behind this decition is security, adding a user to this group is not part of everyday operation, users in that groups has really powerful permissions and you should add users to Service Accounts only in really specific situations and only when really required. This is the reason why you need to resort to Command Line.

tfssecurity 
	/g+ 
	"Project Collection Service Accounts" 
	alkampfer@nablasoft.com 
	/collection:https://gianmariaricci.visualstudio.com/DefaultCollection

TfsSecurity.Exe command line utility can add whatever users to whatever group, bypassing limitation in the UI. Remember than to remove the user from that group when he does not need anymore special permission; the commandline is the same as previous one just change /g+ to /g-

As a rule of  thumb, users should only be added to Service Account group only if strictly required, and removed from that group immediately after the specific need ceased to exist.

In older version of VSO / TFS you could obtain the same result without command line in the UI. You just selected the user you want to add to Service Group, then go to the member of section and then, pressing plus button, add the user to the group, but this is actually disabled in actual version.

image

Figure 2: You cannot add anymore a user directly to a group.

If you really want to avoid command line, you can still use the UI. Just create a standard TFS Group and then add the group to the Project Collection Service Accounts. First step: create a group with a Really Explicit Name.

image

Figure 3: This group has a specific name that immediately tells to the reader that it is a special group.

Once the group is created, you can simply add it to the Project Collection Service Account group with few click.

image

Figure 4: Add new group to the Project Collection Service Accounts group

Now you can simply add and remove users to the “WARN – Service Account Users” group from the UI when you need to grant or remove Service Account Permission.

Gian Maria Ricci.

Make easy storing secure password in TFS Build with DPAPI

I’ve blogged some days ago on Securing the password in build definition. I want to make a disclaimer on this subject. The technique described in that article permits you to use encrypted password in a build definition, but this password cannot be decrypted only if you have no access to the build machine. If you are a malicious user and you can schedule a build, you can simply schedule a new build that launch a custom script that decrypts the password and sends clear password by email or dump to the build output.

The previous technique is based on encrypting with DPAPI, encrypted password can be decrypted only by TfsBuild user and only in the machine used to generate the password (build machine). Despite the technique you used to encrypt the password, the build process should be able to decrypt the password, so it is possible for another user to schedule another build running a script that decrypt the password.

Every user that knows the TfsBuild user password can also remote desktop to build machine, or using Powershell Remoting to decrypt the password from the build server. This means: the technique described is not 100% secure and you should be aware of limitation.

Apart from these discussions on the real security of this technique, one of the drawbacks of using DPAPI is you need to do some PowerShell scripting in the remote machine to encrypt the password. So you need to remote Desktop build machine or you need to do a remote session with PowerShell. A better solution is creating a super simple asp.net Site that will encrypt the password with a simple HTML page, then deploy that site on the Build Server.

The purpose is having a simple page running on build server with credentials of TfsBuild that simply encrypt a password using  DPAPI

image

Figure 1: Simple page to encrypt a string.

You can test locally this technique simply running the site in localhost using the same credentials of logged user, encrypting a password and then try to decrypt in powershell.

image

Figure 2: Decrypting a password encrypted with the helper site should work correctly.

The code of this page is really stupid, here is the controller.

[HttpPost]
public ActionResult Index(String pwd)
{
    var pbytes = Protect(Encoding.Unicode.GetBytes(pwd));
    ViewBag.Encrypted = BitConverter.ToString(pbytes).Replace("-", "");
    return View();
}

public static byte[] Protect(byte[] data)
{
    try
    {
        // Encrypt the data using DataProtectionScope.CurrentUser. The result can be decrypted 
        //  only by the same current user. 
        return ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
    }
    catch (CryptographicException e)
    {
        Console.WriteLine("Data was not encrypted. An error occurred.");
        Console.WriteLine(e.ToString());
        return null;
    }
}

And the related view.

@{
    ViewBag.Title = "Index";
}

<h2>Simple Powershell Encryptor utils</h2>
<form method="post">


    Insert your string <input type="password" name="pwd" />
    <br />
    <input type="submit" value="Encrypt" />
    <br />
    <textarea cols="80
              " rows="10" >@ViewBag.Encrypted</textarea>

</form>

Thanks to this simple site encrypting the password is much more simpler than directly using powershell and you do not need to remote desktop to build machine. To have a slightly better security you can disable remote desktop and remote powershell in the Build Machine so noone will be able to directly use PowerShell to decrypt the password, even if they know the password of TfsBuild user.

Related Articles

Gian Maria.

Is there a reason to put restriction on password?

I’ve stumbled upon this funny comic

 

I usually use long Random generated password, that I store in KeePass for all services that I really care about, (home banking, amazon account that has my credit card, etc), and tend to use easy to remember password for services I do not care very much (stupid online games, or stuff like that).

This funny comics suggests that choosing some four random common words can be a viable solution (complex to guess, but easy to remember), but sadly enough some online services does not permits you to use long password, or password that use special chars etc. My online banks forced me to choose a 10 digit number as the password o_O, another online service told me to use a password between 6 and 18 chars, but only letters, numbers are allowed, Another one forced me to use at leas one uppersize, and one digit, but limits the length to 20 chars, etc etc. My question is “why in the hell a service should limit my possibilities to choose a password I like?”.

Having such restrictions is quite annoying, because if you have a mental scheme to choose passwords, password complexity rules quite often render this scheme not valid, forcing you to use a password that will be hard to remember (thanks to keepass this is much more easier) and not more secure. And what about a Chinese or Japanese user that want to choose a password composed of Kanji characters? Maybe he want to use Kanji of Spring, mountain, sky, because it is easy for her to remember a blue mountain sky on spring.

I’m not a cryptography expert, but usually password are stored in HASHED format with a SALT (beware of Italian Railway system, last year I clicked “lost my password” and they sent me the password in CLEAR format on my e-mail O_o), this means that the user could choose an arbitrary sequence of Unicode chars, because it is simply a stream of bytes that will be hashed producing another stream of bytes of Fixed Length that can be stored in a database without problems, even if the user choose a 100 character password, the hash length is always the same.

Given this, is there really a reason to impose restrictions on password complexity? In my opinion the only restriction should be in the length, prohibiting really short password to avoid really easy-to-guess password, but every Unicode charachter should be acceptable and there should be no maximum password length, no specific char requirements (es. at least one digit, at least one Uppercase char), if I trust my KeePass program to generate a cryptography random sequence of 32 chars, or if I want to use an Haiku I like, why you should limit my freedom in choosing my password?

Alk.

How to check if a user belong to a certain role in ASP.Net

This question is really simple to answer… or no? Suppose you need to verify, in a service, if the user belongs to the xxxx group, and then take a different path of execution if the condition is true.

if (Roles.IsUserInRole("xxxx"))

{

    ...

}

Ok, this seems such a piece of innocent code, but actually it caused me a bad bug. The reason is really simple, the same service is called from a program written in windows forms, (a windows service) and a web site. The programmer that is developing the web site, took the service and add that checks in one function, and I begin to get exception from the code of the service. The reason is clear Asp.Net roles and Membership are not configured in the windows program, nor I want to configure it.

To fix this, and in general to verify roles of the current user, when you does not know in advance if the code would be called  outside the context of a web application, we need to create the group xxxx in windows, assign the user that run the service or the program to this group and add this line at the very beginning of the winform program to use windows authentication.

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

But this is not enough since the code in the service should not use the Roles.IsUserInRole() function because it needs to have membership configuration enabled. The solution is using this code instead of the above one.

var currentPrincipal = System.Threading.Thread.CurrentPrincipal;

if (currentPrincipal.IsInRole("xxxx")

This works because in windows forms the CurrentPrincipal is a WindowsPrincipal, and thus it checks if the current user belong to the group xxxx, but when it is run from a web site, with ASP.NEt membership configured, the principal is of type RolePrincipal as you can verify from Fgure1

image

Figure 1: the current principal of code running in asp.net sites with roles and membership configured is of type RolePrincipal.

and in this situation IsInRole() method verifies the user against ASP.Net roles.

Alk.