Skip to content

Allow GET in webhook triggers#56446

Merged
frenck merged 1 commit intohome-assistant:devfrom
swiergot:webhook_get2
Apr 14, 2023
Merged

Allow GET in webhook triggers#56446
frenck merged 1 commit intohome-assistant:devfrom
swiergot:webhook_get2

Conversation

@swiergot
Copy link
Copy Markdown
Contributor

Proposed change

Accept HTTP GET method in webhook triggers. So far only POST, PUT and HEAD have been allowed, but many devices which can be configured to send HTTP commands support only GET.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • The code has been formatted using Black (black --fast homeassistant tests)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.
  • Untested files have been added to .coveragerc.

The integration reached or maintains the following Integration Quality Scale:

  • No score or internal
  • 🥈 Silver
  • 🥇 Gold
  • 🏆 Platinum

To help with the load of incoming pull requests:

@homeassistant
Copy link
Copy Markdown
Contributor

Hi @swiergot,

It seems you haven't yet signed a CLA. Please do so here.

Once you do that we will be able to review and accept this pull request.

Thanks!

Copy link
Copy Markdown
Contributor

@elliotmoso elliotmoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@MartinHjelmare
Copy link
Copy Markdown
Member

See these rejected PRs for history in this matter:
#17782
#19648

@frenck
Copy link
Copy Markdown
Member

frenck commented Sep 27, 2021

I think we should maybe consider allowing for this, for 2 reasons:

  • As said in this and previous PRs, some devices may only support GET requests
  • Webhooks can be used to trigger "anything", and doesn't have to provide information even. I can see possibilities to use simple GET requests to trigger automation.

@esev
Copy link
Copy Markdown
Contributor

esev commented Dec 16, 2021

If this PR is to apply to all webhooks, is it possible to add CSRF protection on these URLs?

@davet2001
Copy link
Copy Markdown
Contributor

+1 from me. QR codes and RFID tag URLs could then be used to trigger automations without apps.

@davet2001
Copy link
Copy Markdown
Contributor

@esev what type of CSRF protection did you have in mind? I don't think any of the use cases wanting this can set header tokens.

@emufan

This comment has been minimized.

@esev
Copy link
Copy Markdown
Contributor

esev commented Feb 3, 2022

@esev what type of CSRF protection did you have in mind? I don't think any of the use cases wanting this can set header tokens.

I agree. And I don't see any good solution except for using long random URLs that aren't shared publicly.

Just to clarify. I do see the value in this PR! It makes some integrations a lot simpler. My goal isn't to stop the PR. I just want to raise awareness of the issues. As long as users are aware of the abuse vector, they can take steps to avoid it.

I just fear folks will use this without considering the consequences. http://homeassistant.local:8123/api/webhook/ is a fairly common URL that will work on many people's HA instances. These are unauthenticated URLs. It doesn't take much to abuse this if folks aren't always generating secret random webhook IDs, or if they print the URLs on QR codes/NFC stickers. Any web page or email that contains html similar to the following will be enough. It doesn't matter where the html is hosted; all that matters is where the user's browser is running.

<html>
  <body>
    <img src="http://homeassistant.local:8123/api/webhook/Unlock_Door" />
    <img src="http://homeassistant.local:8123/api/webhook/Open_Garage" />
  </body>
</html>

Can a blueprint contain a webhook trigger? If so that might be another abuse vector to consider (blueprints with common webhook names, or attacker controlled blueprints). Especially with the easy-to-click "Add Blueprint" buttons.

That said, AFAICT the CSRF issue is already present with webhooks as the CORS headers already allow this (cors_allowed = True). This PR just makes it easier to abuse directly from any static email/web page; no javascript required.

This might get safer in the future though. See https://wicg.github.io/private-network-access/. I believe Chrome intends to support this (https://developer.chrome.com/blog/private-network-access-update/).

@emufan
Copy link
Copy Markdown
Contributor

emufan commented Feb 3, 2022

is a fairly common URL that will work on many people's HA instances

But you are aware that everything you wrote is the same for POST webhooks?

This request is to get GET and not to remove webhooks. 😉

@davet2001

This comment was marked as off-topic.

@emufan
Copy link
Copy Markdown
Contributor

emufan commented Feb 3, 2022

Why are we discussing webhooks in this PR? This is completely not related to it.

Everything what you wrote is already there. Warning, off by default, created only if you create an automation, etc.

https://www.home-assistant.io/docs/automation/trigger/#webhook-trigger

And I saw this "only local" in the changelog from 2022.2.0 as well. At least I think so.

@esev
Copy link
Copy Markdown
Contributor

esev commented Feb 3, 2022

The difference in this PR is that GET requests have no CORS preflight checks. The GET verb was intended to be a read-only action without side-effects. By adding GET, the webhooks are automatically triggerable from any static web page. That was not possible before this PR; you had to use some javascript or user interaction to trigger the webhook from a web page.

Example: With this PR, if I add a webhook with the ID Open_Garage. All a bad actor needs to do is trigger a GET to http://homeassistant.local:8123/api/webhook/Open_Garage. This can be in an email, a malicious ad, a URL shortener service, a PDF/document, a wiki/forum post that allows images, or any website. The user only needs to open the web page from a device on their local network, and doesn't need to interact with the web page. And no javascript is required. This bypasses the browser's built-in security mechanisms. And there was no indication in the UI that this might be unsafe.
image

This can't be prevented by checking if the device is on the local subnet. Until https://wicg.github.io/private-network-access/ is implemented in browsers, all that is required is that your browser (likely on the same subnet as HA) be able to access Home Assistant locally. And that only considers browsers. It could still be triggered by malicious content in document/PDF viewers, native apps, chat messages, etc.

Admittedly though, I think most browsers would block this behavior from secure sites. Browsers tend to not allow mixed content; http requests from https sites. So that would mitigate the risk a bit. But it would be great if we could also consider users who setup SSL for themselves too, and make them safe also.

Personally I'd like to see:

  • The cors_allowed = True be an option on each webhook individually, and for the default to be False (not related to this PR, but it would close the pre-existing CSRF browser-based security issue by default)
  • Another per-webhook option (Allow Web Browsers) to allow common browser User Agents to be permitted to trigger the webhook. And for that option to default to False. Or, as @davet2001 suggested, only allow specific User Agents. (Could be used to make this PR safer, but not a bullet-proof solution)
  • For this PR specifically: A third per-webhook option to allow 'GET' requests to trigger a webhook, as these bypass the CORS preflight checks in the browser. Again, I'd like to see this default to False.
  • Add an option to restrict the webhook to a specific IP address/ranges (suggested by @davet2001). Maybe default to the local subnet, but with the understanding that it doesn't provide protection for browsers in the default state (could be used to make this PR safer as long as the device at that IP isn't running a browser. Would be a big win for users who expose HA directly to the internet. Probably needs to consider reverse proxies: 'X-Forwarded-For')
  • Provide a default, randomly-generated, webhook ID in the UI. Add a warning in the UI if the webhook ID doesn't contain much entropy/randomness. Similar to how some websites have password complexity tests. (not directly related to this PR)
  • Disallow a webhook ID to be defined in a blueprint. Randomly generate one or let the user choose one. (assumes blueprints can define webhook triggers - I haven't verified this. This isn't directly related to this PR)

Each of these options could be described in the docs with examples of how they could be abused if enabled.

I'm not on the HA core development team, so don't take anything I've written as a requirement for this PR. I'm not suggesting the PR be blocked; I see how it is very useful. I'm only suggesting ideas to make things safe by default (from a browser security perspective) when using webhooks so users can enable them without adding security risks. IMHO I don't think users understand/consider the CSRF implications for browsers before using webhooks. I'd just like users to be aware of the danger, and need to perform an extra action, before using webhooks in an unsafe way. In it's current form, this PR increases the danger by bypassing CORS preflight checks in the browser, without providing any safe-by-default options. This increases risk for all previously defined webhooks that only expected POST/PUT/HEAD.

@alderete
Copy link
Copy Markdown

alderete commented Feb 5, 2022

As someone who has requested GET support for webhooks, in several places, I think the additions proposed by @esev all sound like good ideas. In particular, the last four bullets:

  • Per-webhook option for GET — false by default
  • Per-webhook option for array of allowed IP addresses — local subnet by default?
  • In the Automation editor UI, generate a random webhook ID by default (but allow me to override)
  • Disallow webhook ID in blueprints, replace with random ID

All sound like terrific, and hopefully not terribly complicated additional development.

I'm sad that that might mean this PR requires some re-work. I have Shelly Button 1's that want GET webhooks today to make it easier for my wife to turn off the "smart" bedroom light switch without getting out of bed. (The switch is by the door; the bed is not.)

But safety first!

I'll personally commit to writing the doc to go with the changes, if the appropriate person wants to loop me in. (I'm a tech writer by day, and I have one commit for the webhooks doc already.)

@esev
Copy link
Copy Markdown
Contributor

esev commented Feb 5, 2022

@alderete I've sent a PR for randomizing the webhook_id by default. home-assistant/frontend#11568 Could you take a look, and update the docs if you think there is anything needed for that?

Screenshot 2022-02-05 5 06 58 PM

The copy button on the right-side copies the URL for the webhook to the clipboard (http://homeassistant.local:8123/api/webhook/bnWvYB5KLRBbkT9XMCl9zXoX). I figured that makes it a bit easier to use too.

Thanks for volunteering to update the docs!

@alderete
Copy link
Copy Markdown

alderete commented Feb 6, 2022

I just submitted a PR for the webhooks doc: home-assistant/home-assistant.io#21506

I attempted to add security details in the form of best practices and considerations (don't open your garage door, etc.), and other concerns discussed in this thread.

And I'm happy to continue to update the docs as this and other open webhook-related PRs move forward. (That is, please don't let doc be the blocker on this PR. I will fix it if that's what's needed.)

@esev
Copy link
Copy Markdown
Contributor

esev commented Feb 7, 2022

I'm putting together a PR to:

  1. Add an option to allow webhook triggers to be accessed from the internet, and making the default False.
  2. Make all HTTP methods optional, and enabling enabling 'POST' & 'PUT' by default.

For 1, I'm making it so that Nabu Casa's webhooks still work regardless & any webhooks triggered via the websocket API still work too. For both of these, the user would have needed to explicitly enable it, so that seems safe to me.

Number 2 will be easy to adapt for this PR, and add 'GET' as an option too.

image

trigger:
  - platform: webhook
    webhook_id: bnWvYB5KLRBbkT9XMCl9zXoX
    allow_methods:
      - PUT
      - POST
    allow_internet: false

I'm going to wait to see if the core developers give any feedback on the two outstanding PRs, or this PR, before proceeding on this next change though. I'll need some help updating the docs and creating a note about this being a breaking change too, assuming this all looks okay.

@balloob
Copy link
Copy Markdown
Member

balloob commented Feb 8, 2022

I like the idea of opt-in GET. We should make sure it applies to the hooks coming in via cloud/websocket too.

@davet2001
Copy link
Copy Markdown
Contributor

@byroncoetsee @FutureTense I don't think anyone is objecting to this feature.

It's just not been reviewed yet.

You could help by reviewing the PRs - even a partial review helps. 😀

Copy link
Copy Markdown

@byroncoetsee byroncoetsee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me - pretty straight forward 😃

@byroncoetsee
Copy link
Copy Markdown

@davet2001 reviews added and awaiting merge... 👍🏼

@esev
Copy link
Copy Markdown
Contributor

esev commented Oct 2, 2022

Are we saying that it's better to strip ability and responsibility rather than give someone an opt-in feature (which could have like 3 warning popups if you're that concerned about this stranger's inability to make their own decisions)?

I'd like to have this as an opt-in feature so that folks who previously setup webhooks are not impacted by this change. This PR will decrease security for all previously added webhooks. That's why I proposed the other PRs, to give users an option to enable GET if they need it.

@CR6-SE

This comment was marked as spam.

@byroncoetsee

This comment was marked as spam.

@CR6-SE

This comment was marked as spam.

@frenck frenck self-requested a review January 6, 2023 11:25
@frenck frenck self-assigned this Jan 6, 2023
@Anks329

This comment was marked as off-topic.

@Holland1

This comment was marked as off-topic.

@mucki12

This comment was marked as off-topic.

@frenck frenck requested a review from a team as a code owner April 12, 2023 14:02
@frenck
Copy link
Copy Markdown
Member

frenck commented Apr 12, 2023

I've rebased the PR onto the latest dev branch (it was 21815 commits behind) and resolved the merge conflict.

@frenck frenck added the noteworthy Marks a PR as noteworthy and should be in the release notes (in case it normally would not appear) label Apr 12, 2023
@pvizeli
Copy link
Copy Markdown
Member

pvizeli commented Apr 12, 2023

I think before we add this, we should consider: #66494

@frenck
Copy link
Copy Markdown
Member

frenck commented Apr 12, 2023

@pvizeli yes, that has been discussed and is next up.

Copy link
Copy Markdown
Member

@frenck frenck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @swiergot 👍

../Frenck

@frenck
Copy link
Copy Markdown
Member

frenck commented Apr 12, 2023

This PR is good to go, but marking it as draft until #66494 is ready to go as well.
The aim is to get them in the same release.

@frenck frenck marked this pull request as draft April 12, 2023 17:22
@frenck frenck marked this pull request as ready for review April 14, 2023 07:44
@frenck
Copy link
Copy Markdown
Member

frenck commented Apr 14, 2023

Alright going to merge this one in, now #66494 is ready to go too.
Thanks again @swiergot 👍

../Frenck

@frenck frenck merged commit 47f5160 into home-assistant:dev Apr 14, 2023
@epenet
Copy link
Copy Markdown
Contributor

epenet commented Apr 14, 2023

This appears to have broken CI

Error: tests/components/webhook/test_init.py:183:11: F811 Redefinition of unused `test_webhook_get` from line 134

Edit: not this PR.

@github-actions github-actions bot locked and limited conversation to collaborators Apr 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

cla-signed core has-tests integration: webhook new-feature noteworthy Marks a PR as noteworthy and should be in the release notes (in case it normally would not appear) small-pr PRs with less than 30 lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.