[epiphany] Warn user about insecure password forms



commit 5e8b6f9bc985acef72df2bec1a451a1339b75410
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Sat Nov 26 22:06:35 2016 -0600

    Warn user about insecure password forms
    
    https://bugzilla.gnome.org/show_bug.cgi?id=775167

 embed/ephy-embed-shell.c                 |   44 ++++++++++++++++++++++++++
 embed/ephy-web-view.c                    |   49 ++++++++++++++++++++++++++++++
 embed/web-extension/ephy-web-dom-utils.c |   41 +++++++++++++++++++++++++
 embed/web-extension/ephy-web-dom-utils.h |    2 +
 embed/web-extension/ephy-web-extension.c |   46 ++++++++++++++++++++++++++++
 lib/ephy-security-levels.c               |    6 ++++
 lib/ephy-security-levels.h               |    2 +
 7 files changed, 190 insertions(+), 0 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 73250cc..2ecc330 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -72,6 +72,7 @@ enum {
   PAGE_CREATED,
   ALLOW_TLS_CERTIFICATE,
   FORM_AUTH_DATA_SAVE_REQUESTED,
+  SENSITIVE_FORM_FOCUSED,
 
   LAST_SIGNAL
 };
@@ -139,6 +140,26 @@ web_extension_form_auth_data_message_received_cb (WebKitUserContentManager *mana
 }
 
 static void
+web_extension_sensitive_form_focused_message_received_cb (WebKitUserContentManager *manager,
+                                                          WebKitJavascriptResult   *message,
+                                                          EphyEmbedShell           *shell)
+{
+  guint64 page_id;
+  gboolean insecure_action;
+  GVariant *variant;
+  char *message_str;
+
+  message_str = ephy_embed_utils_get_js_result_as_string (message);
+  variant = g_variant_parse (G_VARIANT_TYPE ("(tb)"), message_str, NULL, NULL, NULL);
+  g_free (message_str);
+
+  g_variant_get (variant, "(tb)", &page_id, &insecure_action);
+  g_signal_emit (shell, signals[SENSITIVE_FORM_FOCUSED], 0,
+                 page_id, insecure_action);
+  g_variant_unref (variant);
+}
+
+static void
 history_service_query_urls_cb (EphyHistoryService *service,
                                gboolean            success,
                                GList              *urls,
@@ -767,6 +788,12 @@ ephy_embed_shell_startup (GApplication *application)
                     shell);
 
   webkit_user_content_manager_register_script_message_handler (priv->user_content,
+                                                               "sensitiveFormFocused");
+  g_signal_connect (priv->user_content, "script-message-received::sensitiveFormFocused",
+                    G_CALLBACK (web_extension_sensitive_form_focused_message_received_cb),
+                    shell);
+
+  webkit_user_content_manager_register_script_message_handler (priv->user_content,
                                                                "aboutApps");
   g_signal_connect (priv->user_content, "script-message-received::aboutApps",
                     G_CALLBACK (web_extension_about_apps_message_received_cb),
@@ -1031,6 +1058,23 @@ ephy_embed_shell_class_init (EphyEmbedShellClass *klass)
                   G_TYPE_UINT64,
                   G_TYPE_STRING,
                   G_TYPE_STRING);
+
+  /**
+   * EphyEmbedShell::sensitive-form-focused
+   * @shell: the #EphyEmbedShell
+   * @page_id: the identifier of the web page
+   * @insecure_action: whether the target of the form is http://
+   *
+   * Emitted when a form in a web page gains focus.
+   */
+  signals[SENSITIVE_FORM_FOCUSED] =
+    g_signal_new ("sensitive-form-focused",
+                  EPHY_TYPE_EMBED_SHELL,
+                  G_SIGNAL_RUN_FIRST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 2,
+                  G_TYPE_UINT64,
+                  G_TYPE_BOOLEAN);
 }
 
 /**
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index 90360e5..ad74394 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -101,6 +101,7 @@ struct _EphyWebView {
   GtkWidget *microphone_info_bar;
   GtkWidget *webcam_info_bar;
   GtkWidget *password_info_bar;
+  GtkWidget *sensitive_form_info_bar;
 
   EphyHistoryService *history_service;
   GCancellable *history_service_cancellable;
@@ -736,6 +737,39 @@ form_auth_data_save_requested (EphyEmbedShell *shell,
 }
 
 static void
+sensitive_form_focused_cb (EphyEmbedShell *shell,
+                           guint64         page_id,
+                           gboolean        insecure_action,
+                           EphyWebView    *web_view)
+{
+  GtkWidget *info_bar;
+  GtkWidget *label;
+  GtkWidget *content_area;
+
+  if (web_view->sensitive_form_info_bar)
+    return;
+  if (webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)) != page_id)
+    return;
+  if (insecure_action || ephy_security_level_is_secure (web_view->security_level))
+    return;
+
+  /* Translators: Message appears when insecure password form is focused. */
+  label = gtk_label_new (_("Heads-up: this form is not secure. If you type your password, it will be visible 
to cybercriminals!"));
+  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+
+  info_bar = gtk_info_bar_new ();
+  content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+  gtk_container_add (GTK_CONTAINER (content_area), label);
+  gtk_widget_show (label);
+
+  ephy_web_view_track_info_bar (info_bar, &web_view->sensitive_form_info_bar);
+
+  ephy_embed_add_top_widget (EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (web_view),
+                             info_bar, FALSE);
+  gtk_widget_show (info_bar);
+}
+
+static void
 allow_tls_certificate_cb (EphyEmbedShell *shell,
                           guint64         page_id,
                           EphyWebView    *view)
@@ -772,6 +806,10 @@ page_created_cb (EphyEmbedShell        *shell,
                            G_CALLBACK (form_auth_data_save_requested),
                            view, 0);
 
+  g_signal_connect_object (shell, "sensitive-form-focused",
+                           G_CALLBACK (sensitive_form_focused_cb),
+                           view, 0);
+
   g_signal_connect_object (shell, "allow-tls-certificate",
                            G_CALLBACK (allow_tls_certificate_cb),
                            view, 0);
@@ -802,6 +840,11 @@ ephy_web_view_dispose (GObject *object)
     view->password_info_bar = NULL;
   }
 
+  if (view->sensitive_form_info_bar) {
+    g_object_remove_weak_pointer (G_OBJECT (view->sensitive_form_info_bar), (gpointer 
*)&view->sensitive_form_info_bar);
+    view->sensitive_form_info_bar = NULL;
+  }
+
   g_signal_handlers_disconnect_by_func (view->history_service,
                                         ephy_web_view_history_cleared_cb,
                                         EPHY_WEB_VIEW (object));
@@ -1634,6 +1677,12 @@ load_changed_cb (WebKitWebView  *web_view,
       /* Zoom level. */
       restore_zoom_level (view, loading_uri);
 
+      if (view->sensitive_form_info_bar) {
+        g_object_remove_weak_pointer (G_OBJECT (view->sensitive_form_info_bar), (gpointer 
*)&view->sensitive_form_info_bar);
+        gtk_widget_destroy (view->sensitive_form_info_bar);
+        view->sensitive_form_info_bar = NULL;
+      }
+
       break;
     }
     case WEBKIT_LOAD_REDIRECTED:
diff --git a/embed/web-extension/ephy-web-dom-utils.c b/embed/web-extension/ephy-web-dom-utils.c
index ccc0146..41c8376 100644
--- a/embed/web-extension/ephy-web-dom-utils.c
+++ b/embed/web-extension/ephy-web-dom-utils.c
@@ -541,6 +541,47 @@ ephy_web_dom_utils_find_form_auth_elements (WebKitDOMHTMLFormElement *form,
   return FALSE;
 }
 
+/* ephy_web_dom_utils_find_form_auth_elements() is great, but it only returns
+ * something useful if it can pair a username node with a password node. This
+ * function says whether the form has a password node or not, even if Epiphany
+ * isn't smart enough to match it to a username node.
+ *
+ * Ideally we'd also detect credit card data or other sensitive stuff, but
+ * there's no standard way to do so, so don't try.
+ */
+gboolean
+ephy_web_dom_utils_form_contains_sensitive_element (WebKitDOMHTMLFormElement *form)
+{
+  WebKitDOMHTMLCollection *elements;
+  guint i, n_elements;
+  gboolean found_auth_element = FALSE;
+
+  elements = webkit_dom_html_form_element_get_elements (form);
+  n_elements = webkit_dom_html_collection_get_length (elements);
+
+  for (i = 0; i < n_elements && !found_auth_element; i++) {
+    WebKitDOMNode *element;
+    char *element_type;
+
+    element = webkit_dom_html_collection_item (elements, i);
+    if (!WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element))
+      continue;
+
+    g_object_get (element, "type", &element_type, NULL);
+
+    if (g_strcmp0 (element_type, "password") == 0 ||
+        g_strcmp0 (element_type, "adminpw") == 0) {
+      found_auth_element = TRUE;
+    }
+
+    g_free (element_type);
+  }
+
+  g_object_unref (elements);
+
+  return found_auth_element;
+}
+
 /**
  * ephy_web_dom_utils_get_absolute_position_for_element:
  * @element: the #WebKitDOMElement.
diff --git a/embed/web-extension/ephy-web-dom-utils.h b/embed/web-extension/ephy-web-dom-utils.h
index 85c3584..ebe4c6d 100644
--- a/embed/web-extension/ephy-web-dom-utils.h
+++ b/embed/web-extension/ephy-web-dom-utils.h
@@ -39,6 +39,8 @@ gboolean ephy_web_dom_utils_find_form_auth_elements (WebKitDOMHTMLFormElement *f
                                                      WebKitDOMNode           **username,
                                                      WebKitDOMNode           **password);
 
+gboolean ephy_web_dom_utils_form_contains_sensitive_element (WebKitDOMHTMLFormElement *form);
+
 void ephy_web_dom_utils_get_absolute_bottom_for_element (WebKitDOMElement *element,
                                                          double           *x,
                                                          double           *y);
diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c
index c89ebd8..c176b90 100644
--- a/embed/web-extension/ephy-web-extension.c
+++ b/embed/web-extension/ephy-web-extension.c
@@ -971,6 +971,46 @@ username_node_input_cb (WebKitDOMNode  *username_node,
   return TRUE;
 }
 
+static gboolean
+sensitive_form_focused_cb (WebKitDOMHTMLFormElement *form,
+                           WebKitDOMEvent           *dom_event,
+                           WebKitWebPage            *web_page)
+{
+  WebKitDOMDOMWindow *dom_window;
+  GVariant *variant;
+  char *message;
+  char *action;
+  gboolean insecure_action;
+
+  dom_window = webkit_dom_document_get_default_view (webkit_web_page_get_dom_document (web_page));
+  if (dom_window == NULL)
+    return FALSE;
+
+  action = webkit_dom_html_form_element_get_action (form);
+  /* The goal here is to detect insecure forms on secure pages. The action need
+   * not necessarily contain any protocol, so just assume the form is secure
+   * unless it's clearly not. Insecure forms on insecure pages will be detected
+   * in the UI process. Note that basically no websites should actually be dumb
+   * enough to trip this, but no doubt they exist somewhere.... */
+  insecure_action = action == NULL && g_str_has_prefix (action, "http://";);
+
+  variant = g_variant_new ("(tb)",
+                           webkit_web_page_get_id (web_page),
+                           insecure_action);
+  message = g_variant_print (variant, FALSE);
+
+  if (!webkit_dom_dom_window_webkit_message_handlers_post_message (dom_window, "sensitiveFormFocused", 
message))
+    g_warning ("Error sending sensitiveFormFocused message");
+
+  g_free (action);
+  g_free (message);
+  g_object_unref (dom_window);
+  g_variant_unref (variant);
+
+  /* Hoping FALSE means "propagate" because there is absolutely no documentation for this. */
+  return FALSE;
+}
+
 static void
 form_destroyed_cb (gpointer form_auth, GObject *form)
 {
@@ -1059,6 +1099,12 @@ web_page_form_controls_associated (WebKitWebPage    *web_page,
       g_object_weak_ref (G_OBJECT (form), form_destroyed_cb, form_auth);
     } else
       LOG ("No pre-fillable/hookable form found");
+
+    if (ephy_web_dom_utils_form_contains_sensitive_element (form)) {
+      webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (form), "focus",
+                                                  G_CALLBACK (sensitive_form_focused_cb), TRUE,
+                                                  web_page);
+    }
   }
 }
 
diff --git a/lib/ephy-security-levels.c b/lib/ephy-security-levels.c
index 2cb98d2..56a40dd 100644
--- a/lib/ephy-security-levels.c
+++ b/lib/ephy-security-levels.c
@@ -52,3 +52,9 @@ ephy_security_level_to_icon_name (EphySecurityLevel level)
 
   return result;
 }
+
+gboolean
+ephy_security_level_is_secure (EphySecurityLevel level)
+{
+  return level >= EPHY_SECURITY_LEVEL_STRONG_SECURITY;
+}
diff --git a/lib/ephy-security-levels.h b/lib/ephy-security-levels.h
index ada0d2b..e5e4d14 100644
--- a/lib/ephy-security-levels.h
+++ b/lib/ephy-security-levels.h
@@ -41,4 +41,6 @@ typedef enum
 
 const char *ephy_security_level_to_icon_name (EphySecurityLevel level);
 
+gboolean ephy_security_level_is_secure (EphySecurityLevel level);
+
 G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]