Securing your HTTP Request trigger in Microsoft Flow

With Microsoft Flow, you can start a Flow with REST by using the ‘When a HTTP request is received’ trigger. Since the HTTP endpoint generated by Flow is not secured, practically anyone with the (guessed) HTTP Post URL can trigger your Flow. In this blog, I will explain how you can secure this trigger to make sure your Flow cannot be triggered from unwanted sources.

As I wrote before in my Custom Actionable Messages with Microsoft Flow blog series, the ‘When a HTTP request is received’ will generate a HTTP Post URL when you first save your Flow:

From then on, this HTTP Post URL can be called from anywhere to trigger the Flow.

To show this, I created a real simple Flow that uses a ‘When a HTTP request is received’ trigger and a ‘Compose’ action that will contain the headers of the HTTP request trigger:

Trigger from Postman

After saving this Flow, my HTTP Post URL is generated. I can now call this URL using Postman which will trigger my Flow successfully:

When I look into my Flow history, I can see that the Request Header is filled. I can even see that the Flow is triggered from Postman:

Trigger from Flow

I only want this Flow to be triggered from another Flow in my Office 365 tenant and not from any other Flows or other (external) locations. To demonstrate how to do this, I created another Flow that calls the HTTP Post URL to trigger that Flow:

When I run this Flow, I can see that my other Flow will be triggered again. In the Request Header, I can see that the Flow is now triggered from Flow (Azure Logic Apps):

This is where it becomes interesting. When scrolling down, I can see the attribute ‘x-ms-workflow-name’ which contains the Flow ID of the Flow that triggers this Flow.

Allow the Flow to be triggered only from a specific other Flow

So, to make sure your Flow only runs when triggered from a specific Flow, you should perform a check on this ‘x-ms-workflow-name’ attribute by using a condition. The condition should check if the ‘x-ms-workflow-name’ attribute inside your Request Header is equal to the ID of the Flow that will trigger this Flow. Unfortunately, only the Request Header is available from your Dynamic content screen and not the attributed within your Request header:

That is because each Request header can be different (as you can see above: the Request Header from Postman was different than the one from Flow). To make use of the ‘x-ms-workflow-name’ attribute, you can switch to advanced mode and paste the following line into your window:

@equals(triggerOutputs()['headers']['x-ms-workflow-name'], '<FLOW ID>')

After that, you can switch back to basic mode (or leave it in advanced mode).

If the condition isn’t met, it means that the Flow isn’t triggered correctly, so we need to make sure the Flow doesn’t continue by using the Terminate action inside the ‘If no’ branch of your condition.

Now, when you create another Flow and perform a HTTP Post request to this Flow, you will see that this Flow will be terminated because the ID doesn’t match. When triggered from the correct Flow, it will continue:

What if this attribute doesn’t exist?

There is still one more thing to take in consideration: the ‘x-ms-workflow-name’ attribute does not always exist (e.g. when performing the request from Postman). If this attribute does not exist, the condition will fail:

So we need to check if this attribute is available within our Request Header and combine this with our first Condition.

To combine conditions, you do need the advanced mode for this because Flow does not provide a ‘drag-and-drop’ option for multiple conditions. We need to combine the following conditions into one:

@contains(triggerOutputs()['headers'], 'x-ms-workflow-name')

(by clicking ‘Edit in advanced mode’, your condition will be auto translated to this format)

and

@equals(triggerOutputs()['headers']['x-ms-workflow-name'], '<FLOW ID>')

both conditions have to be met, so we must use the @and() operand:

@and(
	contains(triggerOutputs()['headers'], 'x-ms-workflow-name'),
	equals(triggerOutputs()['headers']['x-ms-workflow-name'], '<FLOW ID>')
)

Make sure you put the check for the ‘x-ms-workflow-name’ attribute on top, otherwise it will check this attribute for the Flow ID before it even knows it doesn’t exist which will cause your Flow to fail.

With this final condition in place, we can check our Flow if it works:

Of course, theoretically it’s still possible to start your Flow from Postman if you build your Request header correcly but the chances are slim to nil that anyone from without your organisation knows all these attributes and their values.

13 Replies to “Securing your HTTP Request trigger in Microsoft Flow”

  1. Great article.
    What about if admins want to secure all http actions and triggers so that none of the users should be able to call external apis which are not allowed. For e.g. the technique mentioned in this article are good for flow makers but what if flow maker’s intentions are not good and does not want to use the technique given in this article to create some harm. Do you have any suggestions to secure these http actions and triggers from admin perspective?
    Thank you.

    0
    0
    1. Unfortunately, Microsoft hasn’t provided us with a way to secure these triggers. That’s why I came up with this. You can block all HTTP requests on your tenant (or on a specific environment) using Power Platform DLP policies, but that will rule out your internal endpoints as well. I don’t know about an overall solution that only allows certain endpoints on your own tenant.

      0
      0
  2. This is only based on arbitrary values that anyone can fake by setting the headers is postman.

    Unless I missed something ?

    This is security not by design but by obscurity.

    Actually, I believe this is a big fail from MS in the implementation.

    0
    0
    1. Yes, the endpoint is randomly generated by the HTTP trigger after first saving the flow. So in theory, you could guess the endpoint and put it into postman. The same goes for the flow ID’s. But the chances are slim to none that anyone would guess the correct endpoint AND flow ID. I agree that it is not quite secure, but it’s better then nothing

      0
      0
    2. Thank you for the post, Rik, but i have a question, it is possible to add something like a token request before the action is called from postman? for example using oauth or token, is that possible?

      0
      0
    3. I don’t think that’s possible, since everything is generated by the flow itself. You may need to create your own trigger if you want to do this, but I don’t know for sure if that’s possible or how to do that

      0
      0
  3. This is very helpful. In fact, I was redirected here through Microsoft support in regard to securing the Http requests trigger :).

    1
    0
  4. Nice read. You can use the origin header too.
    I had done something similar. You can actually put your condition as a trigger condition in the first http trigger event it self (if you click the settings on it you should be able to see it). This way if the trigger condition is not met the flow would not even run.

    2
    0

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.

This site uses Akismet to reduce spam. Learn how your comment data is processed.